Search code examples
c#swaggerswagger-uiasp.net-core-3.1

Requests for Swagger return 404 Not Found


This is an ASP.NET MVC application on .NET Core 3.1, with Swashbuckle.AspNetCore 6.1.4 (latest). When I try to load the Swagger UI at /swagger/index.html, I'm getting a 404. It appears that the Swagger middleware isn't handling the request and it's making its way to the static files middleware instead. What's infuriating is that this used to work just fine, but I can't figure out what changes I made recently would have caused this problem.

My Startup.cs file had this (fairly standard):

app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.RoutePrefix = "swagger";
    options.SwaggerEndpoint($"{SWAGGER_VERSION}/swagger.json", "My API");
    options.EnableDeepLinking();
});
app.UseStaticFiles();

When I noticed the 404 happening, I tried to load the JSON file in my browser, but got the IIS Express 404 page, which had something curious. Even though the HTTP request was for swagger.json, the error page said:

Requested URL: https://localhost:44316/wwwroot/swagger/v1/swagger.js
Physical path: C:\Users\hcheng\source\repos\[xxx]\src\[xxx.yyyy]\wwwroot\swagger\v1\swagger.js

So IIS changed the file extension, and then tried to find a non-existent Javascript file in the wwwroot folder. Note that I also have URL rewriting, but none of my rewrite rules are applicable here (I even commented that out entirely, which didn't change anything).

I was able to solve this by changing the endpoint to be swaggerjson, which works fine. However, this didn't resolve the 404 for index.html. And for this one, it's something similar:

Requested URL: https://localhost:44316/wwwroot/swagger/index.html
Physical path: C:\Users\hcheng\source\repos\[xxx]\src\[xxx.yyyy]\wwwroot\swagger\index.html

I added some dummy middleware into the pipeline, like so:

app.UseSwagger(); // options omitted
app.UseSwaggerUI(); // options omitted
app.Use(async (context, next) => await next()); // breakpoint here
app.UseStaticFiles();
app.Use(async (context, next) => await next()); // breakpoint here

Now when I tried loading /swagger/index.html I hit the first breakpoint, but not the second, which confirms my theory that the Swagger middleware isn't handling the request, and it's the static files one that is doing it instead.

UPDATE: It turns out when I hit these breakpoints, the request is actually for favicon.ico; the request for /swagger/index/html doesn't hit these at all, which means IIS is totally bypassing ASP.NET here.

I really have no idea what I've done to make this not work. This behavior originally started when I had Swashbuckle 5.6.3 installed, but this persists even after upgrading to 6.1.4.

This problem has also extended to my ElmahCore implementation, which indicates that it's probably a larger issue. For Elmah, the CSS and JS requests are also 404s and again, it's the static files middleware handling those requests instead of the Elmah middleware.

Any help would be greatly appreciated.


Solution

  • It turns out this wasn't actually a code issue, but a configuration one. I was right; there was a recent change that caused the problem. This got deployed for the first time last week, running in-process in IIS. As such, this requires a web.config file, which I copied from another one of our websites. In that file, it was intercepting requests for static assets (images, stylesheets, HTML files) and serving them directly from IIS, so that meant that the Swagger middleware wasn't even receiving the requests (the same applies to Elmah).

    <system.webServer>
        <handlers>
            <add name="StaticFileModuleHtml" path="*.html" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
            <add name="StaticFileModuleHtm" path="*.htm" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
            <add name="StaticFileModuleSvg" path="*.svg" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
            <add name="StaticFileModuleJs" path="*.js" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
            [etc]
    

    I just had to comment these out and everything was back to normal. The Eureka moment came from reading https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-5.0#serve-files-from-multiple-locations where it states, "If the IIS static file handler is enabled and the ASP.NET Core Module is configured incorrectly, static files are served."