Host web api in blazor server application

14.6k views Asked by At

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.

4

There are 4 answers

2
MrC aka Shaun Curtis On BEST ANSWER

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.

0
Nathan Phillips On

For .Net 7 or 8 you need two lines in your minimal Program.cs to add a Web.API Controller to a Blazor Server App::

var builder = WebApplication.CreateBuilder(args); // Existing line
builder.Services.AddControllers();                // Must go between CreateBuilder and Build
var app = builder.Build();                        // Existing line
app.MapControllers();                             // Must go between Build and Run
app.Run();                                        // Existing line
2
Greg Gum On

As of .net 6, you only need to add one line of code to program.cs to enable an API Controller to a Blazor Server App:

builder.Services.AddControllers();

Sample Controller:

[ApiController]
[Route("api")]
public class ApiController : Controller {
    private static readonly string[] Summaries = new[]
    {
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

    private readonly ILogger<ApiController> _logger;

    public ApiController(ILogger<ApiController> logger) {
        _logger = logger;
    }

    [HttpGet("GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get() {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

You blazor app will then return a response to an api call of:

 http://yourapp/api/getweatherforecast
0
Jiri Sykora On

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");
        });
    });