I was writing a Rest endpoint and for some reason the rest calls themselves kept returning empty content even though debugging the code revealed that the Response's Content was defined as I expected it to be. The same problem did not manifest itself for similar calls. Eventually, I realized that the difference between the calls that worked and the calls that failed was that the unsuccessful calls attempted to return recursive (types with public properties/fields of that type) or mutually recursive types while the successful calls did not. As a minimal example of the problem I encountered:
[RoutePrefix("")]
public class LoopyController
{
public class Loopy
{
public Loopy Self {get; set;}
}
[HttpGet]
[Route("loopy")]
public HttpResponseMessage LoopyCall()
{
Loopy loopy = new Loopy();
loopy.Self = loopy;
return Request.CreateResponse(loopy)
}
}
A GET call on the loopy endpoint returns an empty response. I would expect it to either hang or throw an exception. I would like to know why ASP.Net exhibits this behavior. I recognize that a solution to this problem is to modify the return value so that it is no longer recursive in anyway, but would like to know if there is a cleaner method of resolving this issue.
Edit:
There's been discussion in the comments about my use of the term 'recursive type'. I am confident going by https://en.wikipedia.org/wiki/Recursive_data_type that Loopy is a recursive type. However, the problem is not so much that the type Loopy is recursive but that the object loopy has a circular reference. The following endpoint:
[HttpGet]
[Route("notsoloopy")]
public HttpResponseMessage NotSoLoopy()
{
Loopy notSoLoopy = new Loopy();
notSoLoopy.Self = null;
return Request.CreateResponse(notSoLoopy);
}
returns the response {} in reply to a GET request. Of course Loopy being recursive enables loopy's having a circular reference.