Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor WebAssembly App - API call from browser - Sorry, there's nothing at this address

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

like image 685
Ogglas Avatar asked Dec 31 '22 14:12

Ogglas


1 Answers

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/')
like image 67
Ogglas Avatar answered Jan 30 '23 10:01

Ogglas