Skip to content

Commit ddf80ab

Browse files
committed
Add IEndpointRouteBuilder.MapBlazorPages extensions to explicitly map blazor pages
Currently, if you try to use Yarp with Blazor pages, it will not work as expected. Both of these currently expect to be the fallback. This adds in an extension method that will allow you to explicitly register blazor pages so they don't require a fallback anymore.
1 parent 5ebf9c0 commit ddf80ab

38 files changed

+1527
-9
lines changed

Microsoft.AspNetCore.SystemWebAdapters.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.RemoteAuth.Forms.Te
6161
EndProject
6262
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SystemWebAdapters.Abstractions", "src\Microsoft.AspNetCore.SystemWebAdapters.Abstractions\Microsoft.AspNetCore.SystemWebAdapters.Abstractions.csproj", "{632E6195-4304-4C67-AABB-7CFC3F9086B6}"
6363
EndProject
64+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorApp", "samples\BlazorApp\BlazorApp.csproj", "{EE62C68E-7471-4594-84C7-AA7B68057597}"
65+
EndProject
6466
Global
6567
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6668
Debug|Any CPU = Debug|Any CPU
@@ -139,6 +141,10 @@ Global
139141
{632E6195-4304-4C67-AABB-7CFC3F9086B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
140142
{632E6195-4304-4C67-AABB-7CFC3F9086B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
141143
{632E6195-4304-4C67-AABB-7CFC3F9086B6}.Release|Any CPU.Build.0 = Release|Any CPU
144+
{EE62C68E-7471-4594-84C7-AA7B68057597}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
145+
{EE62C68E-7471-4594-84C7-AA7B68057597}.Debug|Any CPU.Build.0 = Debug|Any CPU
146+
{EE62C68E-7471-4594-84C7-AA7B68057597}.Release|Any CPU.ActiveCfg = Release|Any CPU
147+
{EE62C68E-7471-4594-84C7-AA7B68057597}.Release|Any CPU.Build.0 = Release|Any CPU
142148
EndGlobalSection
143149
GlobalSection(SolutionProperties) = preSolution
144150
HideSolutionNode = FALSE
@@ -166,6 +172,7 @@ Global
166172
{C4F5601D-8D33-4C95-BCFD-EDEF1DC095B4} = {A1BDA50C-D70B-416C-97F1-74B0649797C5}
167173
{7C994084-D425-487C-9266-413BB0C22695} = {A1BDA50C-D70B-416C-97F1-74B0649797C5}
168174
{632E6195-4304-4C67-AABB-7CFC3F9086B6} = {F9DB9323-C919-49E8-8F96-B923D2F42E60}
175+
{EE62C68E-7471-4594-84C7-AA7B68057597} = {95915611-30BF-4AFF-AE41-5CDC6F57DCF7}
169176
EndGlobalSection
170177
GlobalSection(ExtensibilityGlobals) = postSolution
171178
SolutionGuid = {DABA3C65-9D74-4EB6-9B1C-730328710EAD}

samples/BlazorApp/App.razor

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Router AppAssembly="@typeof(App).Assembly">
2+
<Found Context="routeData">
3+
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
4+
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
5+
</Found>
6+
<NotFound>
7+
<PageTitle>Not found</PageTitle>
8+
<LayoutView Layout="@typeof(MainLayout)">
9+
<p role="alert">Sorry, there's nothing at this address.</p>
10+
</LayoutView>
11+
</NotFound>
12+
</Router>

samples/BlazorApp/BlazorApp.csproj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net7.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<ProjectReference Include="..\ClassLibrary\ClassLibrary.csproj" />
13+
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SystemWebAdapters.CoreServices\Microsoft.AspNetCore.SystemWebAdapters.CoreServices.csproj" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace BlazorApp.Data
2+
{
3+
public class WeatherForecast
4+
{
5+
public DateOnly Date { get; set; }
6+
7+
public int TemperatureC { get; set; }
8+
9+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
10+
11+
public string? Summary { get; set; }
12+
}
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace BlazorApp.Data
2+
{
3+
public class WeatherForecastService
4+
{
5+
private static readonly string[] Summaries = new[]
6+
{
7+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
8+
};
9+
10+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Sample")]
11+
public Task<WeatherForecast[]> GetForecastAsync(DateOnly startDate)
12+
{
13+
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
14+
{
15+
Date = startDate.AddDays(index),
16+
TemperatureC = Random.Shared.Next(-20, 55),
17+
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
18+
}).ToArray());
19+
}
20+
}
21+
}

samples/BlazorApp/Pages/Counter.razor

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@page "/counter"
2+
3+
<PageTitle>Counter</PageTitle>
4+
5+
<h1>Counter</h1>
6+
7+
<p role="status">Current count: @currentCount</p>
8+
9+
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
10+
11+
@code {
12+
private int currentCount = 0;
13+
14+
private void IncrementCount()
15+
{
16+
currentCount++;
17+
}
18+
}

samples/BlazorApp/Pages/Error.cshtml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@page
2+
@model BlazorApp.Pages.ErrorModel
3+
4+
<!DOCTYPE html>
5+
<html lang="en">
6+
7+
<head>
8+
<meta charset="utf-8" />
9+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
10+
<title>Error</title>
11+
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
12+
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
13+
</head>
14+
15+
<body>
16+
<div class="main">
17+
<div class="content px-4">
18+
<h1 class="text-danger">Error.</h1>
19+
<h2 class="text-danger">An error occurred while processing your request.</h2>
20+
21+
@if (Model.ShowRequestId)
22+
{
23+
<p>
24+
<strong>Request ID:</strong> <code>@Model.RequestId</code>
25+
</p>
26+
}
27+
28+
<h3>Development Mode</h3>
29+
<p>
30+
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
31+
</p>
32+
<p>
33+
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
34+
It can result in displaying sensitive information from exceptions to end users.
35+
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
36+
and restarting the app.
37+
</p>
38+
</div>
39+
</div>
40+
</body>
41+
42+
</html>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Diagnostics;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.RazorPages;
4+
5+
namespace BlazorApp.Pages
6+
{
7+
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
8+
[IgnoreAntiforgeryToken]
9+
public class ErrorModel : PageModel
10+
{
11+
public string? RequestId { get; set; }
12+
13+
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
14+
15+
private readonly ILogger<ErrorModel> _logger;
16+
17+
public ErrorModel(ILogger<ErrorModel> logger)
18+
{
19+
_logger = logger;
20+
}
21+
22+
public void OnGet()
23+
{
24+
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
25+
}
26+
}
27+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@page "/fetchdata"
2+
@using BlazorApp.Data
3+
@inject WeatherForecastService ForecastService
4+
5+
<PageTitle>Weather forecast</PageTitle>
6+
7+
<h1>Weather forecast</h1>
8+
9+
<p>This component demonstrates fetching data from a service.</p>
10+
11+
@if (forecasts == null)
12+
{
13+
<p><em>Loading...</em></p>
14+
}
15+
else
16+
{
17+
<table class="table">
18+
<thead>
19+
<tr>
20+
<th>Date</th>
21+
<th>Temp. (C)</th>
22+
<th>Temp. (F)</th>
23+
<th>Summary</th>
24+
</tr>
25+
</thead>
26+
<tbody>
27+
@foreach (var forecast in forecasts)
28+
{
29+
<tr>
30+
<td>@forecast.Date.ToShortDateString()</td>
31+
<td>@forecast.TemperatureC</td>
32+
<td>@forecast.TemperatureF</td>
33+
<td>@forecast.Summary</td>
34+
</tr>
35+
}
36+
</tbody>
37+
</table>
38+
}
39+
40+
@code {
41+
private WeatherForecast[]? forecasts;
42+
43+
protected override async Task OnInitializedAsync()
44+
{
45+
forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
46+
}
47+
}

samples/BlazorApp/Pages/Index.razor

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@page "/"
2+
3+
<PageTitle>Index</PageTitle>
4+
5+
<h1>Hello, world!</h1>
6+
7+
Welcome to your new app.
8+
9+
<SurveyPrompt Title="How is Blazor working for you?" />

samples/BlazorApp/Pages/_Host.cshtml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@page "/"
2+
@using Microsoft.AspNetCore.Components.Web
3+
@namespace BlazorApp.Pages
4+
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
5+
6+
<!DOCTYPE html>
7+
<html lang="en">
8+
<head>
9+
<meta charset="utf-8" />
10+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
11+
<base href="~/" />
12+
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
13+
<link href="css/site.css" rel="stylesheet" />
14+
<link href="BlazorApp.styles.css" rel="stylesheet" />
15+
<link rel="icon" type="image/png" href="favicon.png"/>
16+
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
17+
</head>
18+
<body>
19+
<component type="typeof(App)" render-mode="ServerPrerendered" />
20+
21+
<div id="blazor-error-ui">
22+
<environment include="Staging,Production">
23+
An error has occurred. This application may no longer respond until reloaded.
24+
</environment>
25+
<environment include="Development">
26+
An unhandled exception has occurred. See browser dev tools for details.
27+
</environment>
28+
<a href="" class="reload">Reload</a>
29+
<a class="dismiss">🗙</a>
30+
</div>
31+
32+
<script src="_framework/blazor.server.js"></script>
33+
</body>
34+
</html>

samples/BlazorApp/Program.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using BlazorApp.Data;
2+
3+
var builder = WebApplication.CreateBuilder(args);
4+
5+
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
6+
7+
// Add services to the container.
8+
builder.Services.AddRazorPages();
9+
builder.Services.AddServerSideBlazor();
10+
builder.Services.AddSingleton<WeatherForecastService>();
11+
12+
builder.Services.AddSystemWebAdapters()
13+
.AddJsonSessionSerializer(options => ClassLibrary.RemoteServiceUtils.RegisterSessionKeys(options.KnownKeys))
14+
.AddRemoteAppClient(options =>
15+
{
16+
options.RemoteAppUrl = new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]!);
17+
options.ApiKey = builder.Configuration["RemoteAppApiKey"]!;
18+
})
19+
.AddAuthenticationClient(true)
20+
.AddSessionClient();
21+
var app = builder.Build();
22+
23+
// Configure the HTTP request pipeline.
24+
if (!app.Environment.IsDevelopment())
25+
{
26+
app.UseExceptionHandler("/Error");
27+
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
28+
app.UseHsts();
29+
}
30+
31+
app.UseHttpsRedirection();
32+
33+
app.UseStaticFiles();
34+
35+
app.UseRouting();
36+
37+
app.UseSystemWebAdapters();
38+
39+
app.MapBlazorHub();
40+
41+
// This is the default way to set up Blazor pages
42+
//app.MapFallbackToPage("/_Host");
43+
44+
// Important: Map explicitly so that deep links continue to work when proxying
45+
app.MapBlazorPages("/_Host");
46+
47+
app.MapReverseProxy();
48+
49+
app.Run();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:53068",
7+
"sslPort": 44337
8+
}
9+
},
10+
"profiles": {
11+
"http": {
12+
"commandName": "Project",
13+
"dotnetRunMessages": true,
14+
"launchBrowser": true,
15+
"applicationUrl": "http://localhost:5171",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development",
18+
"ReverseProxy__Clusters__fallbackCluster__Destinations__fallbackApp__Address": "https://localhost:44339"
19+
}
20+
},
21+
"https": {
22+
"commandName": "Project",
23+
"dotnetRunMessages": true,
24+
"launchBrowser": true,
25+
"applicationUrl": "https://localhost:7193;http://localhost:5171",
26+
"environmentVariables": {
27+
"ASPNETCORE_ENVIRONMENT": "Development",
28+
"ReverseProxy__Clusters__fallbackCluster__Destinations__fallbackApp__Address": "https://localhost:44339"
29+
}
30+
},
31+
"IIS Express": {
32+
"commandName": "IISExpress",
33+
"launchBrowser": true,
34+
"environmentVariables": {
35+
"ASPNETCORE_ENVIRONMENT": "Development",
36+
"ReverseProxy__Clusters__fallbackCluster__Destinations__fallbackApp__Address": "https://localhost:44339"
37+
}
38+
}
39+
}
40+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@inherits LayoutComponentBase
2+
3+
<PageTitle>BlazorApp</PageTitle>
4+
5+
<div class="page">
6+
<div class="sidebar">
7+
<NavMenu />
8+
</div>
9+
10+
<main>
11+
<div class="top-row px-4">
12+
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
13+
</div>
14+
15+
<article class="content px-4">
16+
@Body
17+
</article>
18+
</main>
19+
</div>

0 commit comments

Comments
 (0)