Host web api in blazor server application

Viewed 2263

I have added a webapi project to my solution with a controller to do an HttpGet. The Blazor server project can access it fine locally and deployed. I can make a postman call to the service locally, but the deployed version is giving me the Blazor connection failed message from my _Host file. How would I configure webapi access externally without Blazor interfering?

I think it may be a routing issue of some kind that I need to work out. I am using .NET5 and my webapi startup has this:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "myapp.WebApi v1"));
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

WebApi Program.cs:

namespace myapp.WebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Blazor Program.cs:

namespace myapp.UI
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

My blazor project Startup:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UsePathBase("/myapp");

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
        //TelemetryDebugWriter.IsTracingDisabled = true;
    }
    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();
    }

    // SD2: app.UseSession();

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

    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    // SD2: app.UseMvc();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapBlazorHub();

        // prior fallback setting
        // endpoints.MapFallbackToPage("/_Host");
        
        // 1/4/22 https://stackoverflow.com/questions/61268117/how-to-map-fallback-in-asp-net-core-web-api-so-that-blazor-wasm-app-only-interc
        endpoints.MapFallbackToPage("{*path:regex(^(?!api).*$)}", "/_Host"); // don't match paths beginning with api
    });

}

I am investigating ways to route .../api/... calls to the webapi project and everything else to the blazor project.

In VS, my call that works is https://localhost:44368/api/controller/GetFoos/Year=2020

My deployed call (tried several variations) https://serverName/appName/api/controller/GetFoos/Year=2020

Fails in Blazor. I want Blazor to ignore and have the WebApi handle it.

2 Answers

This is just some code to show how you run API controllers and Blazor on the same site.

You normally add controllers to a Blazor Server project like this:

Add the services

services.AddControllers();

Or if the controllers are in another project/assembly

services.AddControllers().PartManager.ApplicationParts.Add(new AssemblyPart(typeof(Blazr.Database.Controllers.WeatherForecastController).Assembly));

Then set up the middleware pipeline like this:

   app.UseEndpoints(endpoints =>
   {
      endpoints.MapControllers();
      endpoints.MapBlazorHub();
      endpoints.MapRazorPages();
      endpoints.MapFallbackToPage("/_Host");
   });

_Host.cshtml being the Blazor Server SPA launch file.

endpoints.MapControllers(); intercepts and fulfills all requests for the controllers. The Blazor App startup page is the final fallback.

You can also try this (from brief testing it seems working). Try to map fallback inside app.Map.

app.UseEndpoints(endpoints =>
{
    endpoints.MapGrpcService<LocationsServiceV1>().EnableGrpcWeb();

    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

app.Map("",
    builder =>
    {
        builder.UseRouting();
        builder.UseEndpoints(endpoints =>
        {
            // Fallback for blazor front-end
            endpoints.MapFallbackToAreaPage("{*path:nonfile}", "/_Host", "Frontend");
        });
    });
Related