Swagger StackOverflowException

Taking a break from Sqlite, I thought I’d quickly add in Swagger.

So I add the reference, disable CORS, throw in

  Plugins.Add(new SwaggerFeature());

And run my server, getting my https://localhost:12345/swagger-ui/ page up.

But the server then promptly crashes with a StackOverflowException when it’s trying to request the “/resources”

So just before I get into finding which call is eating up the stack, has this come up before or is there something obvious you can think of?

I’ve just tested a Web App with Swagger UI with and without CorsFeature and they’re both working as expected without any StackOverflow Exceptions. I did find an issue with the size of the swagger logo being too big which I’ve just resolved, but functionally everything still works.

Maybe try seeing if the issue persists by replacing SwaggerFeature with the new Open API feature?

OpenApi had the same problem.

Once the HttpResult comes out of ServiceStack.Api.Swagger.Get(SwaggerResources request) it goes though my normal OnAfterExecute() in my Service runner, like everything does.

That calls

response = requestContext.ToOptimizedResult(response);

which is going into an infinite loop.

Not quite sure why, or what I should be checking for to exclude it.

In swagger-client.js isListType() a type of Dictionary<string,string[]> returns “”, which errors in getSignature().

Also, it would be great if you could filter by route path, for something like “/api/…”

I’m assuming one of your DTOs has a cyclical dependency.

Yes, I thought that initially, but actually wasn’t passing my filter in correctly so nothing was being returned.

This snippet on its own causes a StackOverflowException

  var test = new HttpResult(new SwaggerResourcesResponse
  {
    BasePath = requestContext.GetBaseUrl(),
    Apis = new List<SwaggerResourceRef>(),
    ApiVersion = HostContext.Config.ApiVersion,
    Info = new SwaggerInfo
    {
      Title = HostContext.ServiceName,
    }
  })
  {
    ResultScope = () => JsConfig.With(includeNullValues: false)
  };
  requestContext.ToOptimizedResult(test);

I cut down a project to create a working example, here:

Just do

dotnet restore
dotnet build
dotnet test

ToOptimizedResult is only used for serializing a DTO, the above example tries to serialize the HttpResult wrapper which isn’t serializable.

Since the ToOptimizedResult API does the serialization, you can customize the JSON serialization with:

var dto = new SwaggerResourcesResponse
{
    BasePath = requestContext.GetBaseUrl(),
    Apis = new List<SwaggerResourceRef>(),
    ApiVersion = HostContext.Config.ApiVersion,
    Info = new SwaggerInfo
    {
      Title = HostContext.ServiceName,
    }
};

using (JsConfig.With(includeNullValues: false))
{
    return Request.ToOptimizedResult(dto);
}

Then I guess the issue is that I call that in my service runner for anything that comes out of a service and ServiceStack.API.Swagger. is producing that HttpResult.

Should ToOptimizedResult at least check and throw an exception instead of going into an infinite loop?

edit:

My misunderstanding of how to use ToOptimizedResult, which has been in our code base for a while (since 3.x). I googled and see others had had similar issues so I guess it’s a trap to fall into if you aren’t careful, and tracking down any stack error is harder than it ought to be.

I’ve modified ToOptimizedResult() to check for this but trying to guard against every API that accepts a DTO to check if it’s being passed a DTO isn’t a workable strategy. The HttpResult is only for customizing the Service Response and should only be returned in a Service.