6

Update:

Created an issue on Github ASP.NET Core project:

https://github.com/dotnet/aspnetcore/issues/32522

Original:

Created a new Blazor WebAssembly App with Microsoft Visual Studio 2019 Version 16.9.4 with Target Framework .NET 5.0 and ASP.NET Core Hosted.

The problem is that if I make an API call from the browser when the site is published to an Azure Web App I get an error response like this:

Sorry, there's nothing at this address.

enter image description here

If I make the request with "Empty cache and hard reload" everything works as expected.

enter image description here

The request always work on localhost.

It is not limited to images loaded via API, same result for JSON response.

enter image description here

enter image description here

How can I tell Blazor to not use/ignore routing for URLs that contains /api/?

I have read about ASP.NET Core Blazor routing but not found anything there.

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-5.0

The error message is from the App.razor file:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
<MatPortalHost></MatPortalHost>

Same result if I use default value in App.razor:

<Found Context="routeData">
    <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
        <NotAuthorized>
            @if (!context.User.Identity.IsAuthenticated)
            {
                <RedirectToLogin />
            }
            else
            {
                <p>You are not authorized to access this resource.</p>
            }
        </NotAuthorized>
    </AuthorizeRouteView>
</Found>

ImagesController:

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ImagesController : ControllerBase
{
    private readonly ApplicationDbContext _context;

    public ImagesController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpGet("{id}/file")]
    [AllowAnonymous]
    public async Task<ActionResult> GetImageDataFile(int id)
    {
        var image = await _context.Images.FindAsync(id);

        if (image == null)
        {
            return NotFound();
        }

        return File(image.Data, image.ContentType);
    }

Program.cs for Blazor.Client:

namespace Blazor.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddHttpClient("Blazor.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

            builder.Services.AddHttpClient<ImagesHttpClient>(client =>
                client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

            builder.Services.AddHttpClient<PostsAnonymousHttpClient>(client =>
                client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));

            // Supply HttpClient instances that include access tokens when making requests to the server project
            builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Blazor.ServerAPI"));

            builder.Services.AddApiAuthorization();

            builder.Services.AddMatBlazor();

            await builder.Build().RunAsync();
        }
    }
}

Startup.cs for Blazor.Server, not modified at all:

namespace Blazor.Server
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddDatabaseDeveloperPageExceptionFilter();

            services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddIdentityServer()
                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

            services.AddAuthentication()
                .AddIdentityServerJwt();

            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseMigrationsEndPoint();
                app.UseWebAssemblyDebugging();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseIdentityServer();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllers();
                endpoints.MapFallbackToFile("index.html");
            });
        }
    }
}

Publish config looks like this:

enter image description here

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • @BrianParker Done but I'm not sure that will help. I have not modified `Startup.cs` at all and the API Controllers work as long as `Blazor` has not been loaded. The Blazor client should not use the HTTP client configuration either, the request should go directly to the server and return the response without Blazor intervening. – Ogglas May 08 '21 at 22:27
  • I am trying to help. This is odd. It does not happen on localhost. Hrmm can postman hit it reliably? – Brian Parker May 08 '21 at 22:58
  • @BrianParker Much appreciated and I had no intention of being rude. Correct it does not happen on localhost. Postman works without a problem. I do get the same errors trying to load `/.well-known/openid-configuration` and `/_framework/blazor.boot.json` in the browser as well. – Ogglas May 08 '21 at 23:12
  • @BrianParker I don't think so, without `endpoints.MapControllers();` being used the request would not work with Postman or using "Empty cache and hard reload". I agree on hosting configuration but I have no idea what it could be. Preferably I would like to tell Blazor to always let requests to `/api/*` pass without Blazor routing intervening. – Ogglas May 08 '21 at 23:21
  • 1
    Yup I deleted that as soon as I read it to myself hehe. Tried other browsers? – Brian Parker May 08 '21 at 23:23
  • The problem is with your URL(s), the parts you blotted out in all pictures. So go and debug them yourself. There is a mismatch somewhere. – H H May 09 '21 at 11:59
  • I've looked through your code and can't see the code where you're making the API call. Maybe I'm being blind? If not can you show us how you are making the call? – MrC aka Shaun Curtis May 09 '21 at 20:04
  • @HenkHolterman I don't think so. Here is the blotted out URL: https://ogglas.com/api/images/1/file. If you visit it without first loading the domain it will work. If you first load https://ogglas.com then you will see the error above. Tried with different browsers, incognito and different devices and the result is the same. Use `Empty cache and hard reload` and it will work again. – Ogglas May 09 '21 at 21:51
  • @MrCakaShaunCurtis I'm making the API call from browser address bar directly. – Ogglas May 09 '21 at 21:52
  • @Ogglas - no repro. – H H May 10 '21 at 05:20
  • @HenkHolterman If I click on my link above after first loading https://ogglas.com/ I see the message `Sorry, there's nothing at this address.` I have tried it on both my PC and mobile. I have even tried it using mobile 4G to rule out a network issue. What set up did you use? – Ogglas May 10 '21 at 06:41
  • I can alternately click both links and I either get your app or the screenshot. Not the 'nothing here' page. – H H May 10 '21 at 16:00
  • Ok, that was with FireFox. In Chrome I see the same as you. In Edge too. Which makes it interesting. – H H May 10 '21 at 16:05
  • @HenkHolterman Very interesting that you do not see it using Firefox because I have the same message across all browsers. Using Firefox 88.0.1 (64-bit) on Windows 10. Any idea what it could be? – Ogglas May 10 '21 at 18:12

2 Answers2

10

This was a real pain to debug but I eventually found the problem.

TLDR

Remove the line below from Blazor\Client\wwwroot\index.html:

<script>navigator.serviceWorker.register('service-worker.js');</script>

Original:

To have some sort of start I had to reproduce the error locally. I did this by publishing my app to my local IIS instead of IIS Express. There were a few problems with certs and LocalDB but I was able to reproduce the error locally after a while.

Blazor WebAssembly App with Individual Accounts and ASP.NET Core Hosted - IIS - SQLLocalDB 15.0 The specified resource language ID cannot be found

I then tried to create a new application to see if the error existed there. In a new application everything worked as expected. I then tried to step back in Git to see when the error was introduced. I eventually ended up at the Initial commit and the error was still there.

To find the actual error I first tried with WinMerge to compare the folders but in order for that to work I would have to name the projects identically since every namespace would show a diff otherwise.

enter image description here

Luckily there is Meld that can ignore specific words when comparing files and folders. I added two Text Filters under Meld Preferences that matched the namespaces I wanted to ignore and then compared the result.

https://stackoverflow.com/a/46162831/3850405

Now nearly everything was identical except from the Client\wwwroot\ folder. Looking at index.html I noticed a big difference:

enter image description here

When creating the app I must have checked Progressive Web Application due to the serviceWorker above.

enter image description here

When I removed the line everything started working as expected.

<script>navigator.serviceWorker.register('service-worker.js');</script>

I then tried to create a new project with Progressive Web Application set and deployed it to my local IIS with these settings:

enter image description here

After loading the site I could not reach https://localhost/_framework/blazor.boot.json without seeing Sorry, there's nothing at this address..

If you would like to keep using Progressive Web Application and Service worker you can edit service-worker.published.js and update shouldServeIndexHtml to force urls to be server-rendered like this:

&& !event.request.url.includes('/api/')
Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • nice answer, but the last trick didn't help me to solve this anoying issue. I don't understand, because this should solve it. – Yann Feb 14 '22 at 17:19
0

Have you checked your appSetting and the url of your restApi?probably the url of your rest api is still localhost.you must change it to your server published address

  • 1
    Nope - The url can be seen in the comments. View my answer for what fixed it. Turned out to be `Progressive Web Application` and the included `ServiceWorker` – Ogglas May 12 '21 at 16:27