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
CodePudding user response:
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.UseExceptionHandler("/Error");
app.UseHsts();
app.UseSwagger();
app.UseSwaggerUI();
}
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.