Add YARP reverse proxy for /api routes and API config

Integrate YARP to forward all /api/** requests from the browser to the backend EnvelopeGenerator.API service. Add Yarp.ReverseProxy package, configure proxy routes and clusters in appsettings.json, and register YARP in Program.cs. Add API base URL configuration and register ReceiverApiClient, LocalizationService, and ReceiverAuthState as scoped services to support both server and client scenarios. This improves API/UI separation and authentication handling.
This commit is contained in:
2026-05-13 22:47:19 +02:00
parent 4da21133a6
commit 5b90c02a1f
3 changed files with 52 additions and 1 deletions

View File

@@ -19,6 +19,7 @@
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.1-preview.1.25365.4" /> <PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.1-preview.1.25365.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.20" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.20" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,7 +1,10 @@
using EnvelopeGenerator.ReceiverUI.Web.Client.Utils; using EnvelopeGenerator.ReceiverUI.Web.Client.Api;
using EnvelopeGenerator.ReceiverUI.Web.Client.Services;
using EnvelopeGenerator.ReceiverUI.Web.Client.Utils;
using EnvelopeGenerator.ReceiverUI.Web.Components; using EnvelopeGenerator.ReceiverUI.Web.Components;
using System.Text; using System.Text;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
@@ -27,6 +30,26 @@ builder.Services.AddMvc();
builder.Services.AddDevExpressServerSideBlazorPdfViewer(); builder.Services.AddDevExpressServerSideBlazorPdfViewer();
// ── Receiver API + Auth + Localization (for prerender on the server) ─
// During prerendering the components still need the same scoped services
// as on the WASM side. Server-side calls go directly to the API
// (configured via "Api:BaseUrl"); browser-side calls go through YARP.
// Note: server-side prerender does NOT forward the user's auth cookie yet
// (will be added in a later phase if interactive prerender is needed).
var apiBaseUrl = builder.Configuration["Api:BaseUrl"]
?? throw new InvalidOperationException("Api:BaseUrl is missing from configuration.");
builder.Services.AddHttpClient<ReceiverApiClient>(client =>
{
client.BaseAddress = new Uri(apiBaseUrl.TrimEnd('/') + "/");
});
builder.Services.AddScoped<LocalizationService>();
builder.Services.AddScoped<ReceiverAuthState>();
// ── YARP reverse proxy: forwards /api/** from the browser to the API
// (same-origin from the browser's POV → HttpOnly auth cookie flows).
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
@@ -69,6 +92,10 @@ app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseAntiforgery(); app.UseAntiforgery();
// Forward all /api/** traffic (except /api/chat which is handled above)
// to the EnvelopeGenerator.API backend.
app.MapReverseProxy();
app.MapRazorComponents<App>() app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode() .AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode() .AddInteractiveWebAssemblyRenderMode()
@@ -76,3 +103,4 @@ app.MapRazorComponents<App>()
.AllowAnonymous(); .AllowAnonymous();
app.Run(); app.Run();

View File

@@ -4,6 +4,28 @@
"Key": "DEMO", "Key": "DEMO",
"DeploymentName": "gpt-4.1" "DeploymentName": "gpt-4.1"
}, },
"Api": {
"BaseUrl": "https://localhost:8088"
},
"ReverseProxy": {
"Routes": {
"envelope-api": {
"ClusterId": "envelope-api",
"Match": {
"Path": "/api/{**catch-all}"
}
}
},
"Clusters": {
"envelope-api": {
"Destinations": {
"default": {
"Address": "https://localhost:8088"
}
}
}
}
},
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",