6

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.

Steve Greene
  • 12,029
  • 1
  • 33
  • 54

3 Answers3

10

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.

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
  • 1
    Hey, I decided to scrap the separate project and just add the api controller to my existing Blazor app. Now I have a security issue I have added a separate question for here: https://stackoverflow.com/questions/70669528/blazor-server-with-web-api-controller-authenticate-issue – Steve Greene Jan 11 '22 at 15:46
  • Thanks for this. Took me several attempts and searching to finally find this posted and it fixed my routing issues. I had to add the builder.services.AddControllers() in my Programs.cs file. I then commented the original app.MapBlazorHub() and app.MapFallbackToPage("/_Host"). I was then able to use the app.UseEndpoints() function you showed and it fixed my issue. – Caverman Dec 23 '22 at 01:46
5

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
Greg Gum
  • 33,478
  • 39
  • 162
  • 233
1

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");
        });
    });
Jiri Sykora
  • 484
  • 4
  • 10