1

During development, i have used Swagger on the server side of my Blazor WebAssembly App. Always launching (debug) using kestrel instead of IIS Express. Routing worked as expected, all my component routed properly and if i manually typed /swagger, i got to the swagger page. All good.

We have deployed under IIS on our pre-prod servers, the Server side and Blazor WebAssembly App (client) work as expected and are usable, however, my /swagger url gets rewritten (I assume) to go somewhere in my App instead of letting it go to Swagger, obviously there isn't any component that answers to /swagger.

My only guess is that, when hosted on IIS, the aspnet core app takes care of telling IIS what to rewrite and how (similar to the configs that could be provided thru a web.config for a "Standalone" deployment.)

I can't find how to specify exceptions, I've been following the doc at https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/webassembly?view=aspnetcore-3.1#iis

Any idea how i could add an exception for /swagger ?

EDIT:

Turns out it works without issues in Chrome, only Firefox has the unwanted behavior. If i clear my cache, or use Incognito mode, the issue does not happen in Firefox. So, it seems that Firefox caches some stuff and tries to send my URL input to the Blazor Wasm instead of going thru to the server. I will debug some more with the dev tools and fiddler open to try and figure it out, will report back.

Carl Quirion
  • 765
  • 5
  • 25

2 Answers2

5

Turns out there this is part of the service-worker.js file that is published. It is different in dev than what gets published (which makes sense).

During my debugging i was able to reproduce the issue on all browsers (Edge, Chrome and Firefox), regardless of being in Incognito/Private mode or not.

Once the service-worker is running, it handles serving requests from cache/index.html of the Blazor WebAssembly app.

If you go into your Blazor WebAssembly Client "wwwroot" folder, you'll find a service-worker.js and a service-worker.published.js. In the service-worker.published.js, you will find a function that looks like this :

async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === 'GET') {
        // For all navigation requests, try to serve index.html from cache
        // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
        const shouldServeIndexHtml = event.request.mode === 'navigate'
            && !event.request.url.includes('/connect/')
            && !event.request.url.includes('/Identity/');

        const request = shouldServeIndexHtml ? 'index.html' : event.request;
        const cache = await caches.open(cacheName);
        cachedResponse = await cache.match(request);
    }

    return cachedResponse || fetch(event.request);
}

Simply following the instructions found in the code comments is gonna fix the issue. So we ended up adding an exclusion for "/swagger" like so :

&& !event.request.url.includes('/swagger')

Hopefully this post is useful for people who are gonna want to serve things outside of the service worker, not only Swagger.

Carl Quirion
  • 765
  • 5
  • 25
1

Do you have UseSwagger first in your Startup.Configure method?

public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "YourAppName V1")
    );

In Startup.ConfigureServices I have the Swagger code last.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddSwaggerGen(c =>
        c.SwaggerDoc(
            name: "v1",
            info: new OpenApiInfo
            {
                Title = "YourAppName",
                Version = "V1",
            }));
}

This is working just fine for us.

Note: You must navigate to https://yourdomain/swagger/index.html

Peter Morris
  • 20,174
  • 9
  • 81
  • 146
  • Yes, of course. Swagger works when i'm running my app straight from Kestrel (dotnet MyDll.server.dll) but it doesn't work when i'm hosted on IIS because it tries to route /swagger "inside" my Blazor App, looking for a component that would answer to "/swagger" instead of going straight to the swagger page. Which is why I'm talking about URL Rewrite, that the only logical explanation (to me) right now. – Carl Quirion May 29 '20 at 00:54
  • 1
    I was navigating to mydomain/swagger (no index.html), but the same happens if i add index.html. Something funny happened this morning, i went to mydomain/swagger and swagger showed up! After that, with the same tab in Firefox, i deleted /swagger, got to my Blazor wasm site, then retyped /swagger and couldn't get to swagger, was staying in the blazor wasm with a "Sorry, there's nothing at this address." message. I tried the same in Chrome and it actually works as expected in Chrome... So i think the aspnet core/blazor/rewrite side of things is OK and this may be a Firefox 'bug'. – Carl Quirion May 29 '20 at 12:10
  • Posted an answer to my question which details how to specify exceptions to the rewriting/serving. I am currious tho, Peter, do you actually have a Blazor WebAssembly "Hosted Deployment" (that is, the server and the client are hosted on the same IIS site) running on IIS and serving swagger without an issue, after running the WebAssembly App (and it's service worker) ? I mean, it is documented in the service-worker.published.js, which kinda tells me that it is expected that using /swagger, once the serviceworker is running, is not gonna work. Yet you report it "working just fine". – Carl Quirion May 29 '20 at 14:33
  • The final Note: was the key for me (add the `/index.html` to the swagger path) – jramm Jul 04 '20 at 17:46