8

I want to implement both

  • a ASP.NET Core Blazor Server
  • and a ASP.NET Core Web API (the server part, not the consumer/client)

in the same process using .NET 6 and run it self-hosted with Kestrel, i.e. without IIS.

I assume the key is the service and middleware pipeline configuration as found in the according Program.cs templates. Here are the two templates that VS 2022 (17.1.5) creates for me:

For the Blazor Server App:

using BlazorApp1.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
  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.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

For the ASP.NET Core Web API App:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
  app.UseSwagger();
  app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

So the question is how can I combine these two into one?

I want to:

  • Have my program listening to a single port
  • Host the Blazor web pages
  • But process the API when the URL myhost:port/api/.. is being accessed (without interfering with the Blazor part)
  • Have the SwaggerUI, preferably under myhost:port/api/swagger/index.html (again without interfering with the Blazor part)
  • Use the same security mechanism based on a client certificate for both
1
  • 1
    Yeah should be no problem. It's mainly a matter of getting lucky with the right combination of keywords in a Google search so that you can find an example of how to do it. You could probably start at MSDN for a first look and then find YouTube tutorials if you still need more explanation. learn.microsoft.com/en-us/power-apps/developer/data-platform/… Commented Apr 25, 2022 at 9:37

1 Answer 1

10

You should be able to combine these without any issues:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

app.UseRouting();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

The service configuration supports registring all dependencies anyway. And between AddRazorPages() and AddController(), there are quite a lot of shared ones anyway.

The only thing that can be tricky is the pipeline configuration since you need to make sure that both API requests and Blazor requests are handled by the correct handlers.

The good thing is that MapControllers is usually means a fixed set of routes. Since your controller actions have specific routes, having the MapControllers first will make sure that those specific routes will be handled properly by your controllers.

All other requests, those that don’t match any controller action, will then go to the next route handler in the pipeline. So MapBlazorHub is next which will host the SignalR hub for Blazor. That’s also a very specific route which is provided here.

So finally, all remaining requests will land at MapFallbackToPage(). This is a fallback-handler which means that it will handle any route. This allows Blazor to have a single entry-point for which it will then use client-side routing. As long as this fallback call is the last in the pipeline, it should not be able to interfere with any other route handler.

With that combined configuration, both your API and your Blazor should work just fine.

If you do have a situation which is more complicated, then you can use app.MapWhen to branch off the pipeline. There is an example in the documentation if you are interested in this functionality. But as I said, for your situation, you shouldn’t need it.

Sign up to request clarification or add additional context in comments.

6 Comments

Hi @poke. Super. Thank you. In the beginning I was confused because the "app.UseSwagger()" and "app.UseSwaggerUI()" calls are in the "if (!app.Environment.IsDevelopment())" branch. Once I change that so that it's called WHEN IsDevelopment is true it seems to work just fine.
I have a similar scenario as @KarloX except my co-hosted Blazor-Server app would not need to apply proper user account authentication. My Blazor-Server app just provides a developer's diagnostic view of the main web site's internal state and a localhost check would be sufficient authentication. Could app.UseAuthorization(); be made more selective to avoid authentication when a Blazor-Server user session is established?
@camelCase UseAuthorization just tells the pipeline to use the default authorization policy. Since the Blazor Server app is usually just a single Razor Page that the user opens (and which then establishes the Blazor connection), what you can do is give that page a different authorization policy. Check out the docs on how to authorize individual pages.
@poke I tried this with a blazor server 8 application and a .netcore web api but the files in the blazor server could no longer be loaded and I get an error
@kyenry I was able to add an API to a Blazor project by adding only the AddControllers and MapControllers methods in the startup of the Blazor project. This was a .NET 8 Blazor project, with auto render mode (so both Server & WASM).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.