From 97b9bfc82321ab844d9ba0ce4060e3f4e2c8c891 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:35:33 +0000 Subject: [PATCH 1/3] Upgrade AspNetCore React AdvancedSearch sample to .NET 8 - Bump TargetFramework from net6.0 to net8.0 - Bump Microsoft.EntityFrameworkCore.Sqlite/SqlServer 6.0.1 -> 8.0.26 - Replace Microsoft.AspNetCore.SpaServices.Extensions 6.0.1 with Microsoft.AspNetCore.SpaProxy 8.0.26 and add SpaRoot/SpaProxyServerUrl/ SpaProxyLaunchCommand MSBuild properties - Remove System.Data.SqlClient, System.Net.Http, System.Text.RegularExpressions, System.Drawing.Common (functionality is provided by the .NET 8 BCL) - Bump Microsoft.Data.SqlClient 2.1.7 -> 5.2.3 - Bump Microsoft.IdentityModel.JsonWebTokens / System.IdentityModel.Tokens.Jwt 6.34.0 -> 7.7.1 - Replace AddSpaStaticFiles/UseSpaStaticFiles/UseSpa configuration with the SpaProxy template pattern: serve prebuilt SPA from wwwroot via UseStaticFiles + MapFallbackToFile("index.html"); update PublishRunWebpack target to copy ClientApp/build into wwwroot at publish time Co-Authored-By: Toby Drinkall --- ...Demo.AspNetCoreReact.AdvancedSearch.csproj | 136 +++++++++--------- AspNetCore/React/AdvancedSearch/Startup.cs | 24 +--- 2 files changed, 73 insertions(+), 87 deletions(-) diff --git a/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj b/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj index 40bb9e61..feefa608 100644 --- a/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj +++ b/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj @@ -1,69 +1,67 @@ - - - net6.0 - true - Latest - false - ClientApp\ - $(DefaultItemExcludes);$(SpaRoot)node_modules\** - EqDemo.AspNetCoreReact.AdvancedSearch - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %(DistFiles.Identity) - PreserveNewest - - - - \ No newline at end of file + + + net8.0 + true + Latest + false + ClientApp\ + https://localhost:44464 + npm start + $(DefaultItemExcludes);$(SpaRoot)node_modules\** + EqDemo.AspNetCoreReact.AdvancedSearch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wwwroot\%(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + true + + + + diff --git a/AspNetCore/React/AdvancedSearch/Startup.cs b/AspNetCore/React/AdvancedSearch/Startup.cs index 6ea66069..a4ab3390 100644 --- a/AspNetCore/React/AdvancedSearch/Startup.cs +++ b/AspNetCore/React/AdvancedSearch/Startup.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -43,12 +42,6 @@ public void ConfigureServices(IServiceCollection services) services.AddControllersWithViews(); - // In production, the React files will be served from this directory - services.AddSpaStaticFiles(configuration => - { - configuration.RootPath = "ClientApp/build"; - }); - services.AddEasyQuery() .UseSqlManager() .AddDefaultExporters() @@ -78,10 +71,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseCors("AllowAllPolicy"); app.UseHttpsRedirection(); + // In production, prebuilt SPA assets are copied into wwwroot by the + // PublishRunWebpack target and served via the default static-files middleware. app.UseStaticFiles(); - if (!env.IsDevelopment()) { - app.UseSpaStaticFiles(); - } app.UseRouting(); @@ -103,15 +95,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); - }); - - app.UseSpa(spa => - { - spa.Options.SourcePath = "ClientApp"; - if (env.IsDevelopment()) { - spa.UseReactDevelopmentServer(npmScript: "start"); - } + // SPA fallback: serve the prebuilt index.html for client-side routes in production. + // In Development the SPA is launched via Microsoft.AspNetCore.SpaProxy using the + // SpaProxyServerUrl/SpaProxyLaunchCommand MSBuild properties from the .csproj. + endpoints.MapFallbackToFile("index.html"); }); //Init demo database (if necessary) From 0bf81ba0bce7963a04c510f9fb0e85821f39fe04 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:43:00 +0000 Subject: [PATCH 2/3] Align SpaProxyServerUrl with react-scripts default port react-scripts start (npm start) launches the CRA dev server on http://localhost:3000 by default, and ClientApp/ has no .env or setupProxy.js to override that. Setting SpaProxyServerUrl to a non-matching URL (https://localhost:44464) would leave SpaProxy polling forever for a dev server that never appears. Match the URL to CRA's actual default so the dev launch can detect readiness without modifying ClientApp/. Co-Authored-By: Toby Drinkall --- .../EqDemo.AspNetCoreReact.AdvancedSearch.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj b/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj index feefa608..0e3a5176 100644 --- a/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj +++ b/AspNetCore/React/AdvancedSearch/EqDemo.AspNetCoreReact.AdvancedSearch.csproj @@ -5,7 +5,11 @@ Latest false ClientApp\ - https://localhost:44464 + + http://localhost:3000 npm start $(DefaultItemExcludes);$(SpaRoot)node_modules\** EqDemo.AspNetCoreReact.AdvancedSearch From 621d3be4291d74051fd69041d94f05375761e8dd Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:51:12 +0000 Subject: [PATCH 3/3] Activate SpaProxy hosting startup in launchSettings.json Microsoft.AspNetCore.SpaProxy ships its dev-time middleware as a hosting startup assembly, so ASP.NET Core only loads it when its assembly name appears in ASPNETCORE_HOSTINGSTARTUPASSEMBLIES. Without that env var, the SpaProxyStartupFilter is never registered, the React dev server is never auto-launched, and dev-mode requests fall through to MapFallbackToFile("index.html") which 404s in Development (no wwwroot yet). Add the env var to both the IIS Express and EqReactDemo profiles to match the pattern already used by the sibling Angular and Vue3 AdvancedSearch projects (see Properties/launchSettings.json in those projects). Co-Authored-By: Toby Drinkall --- .../React/AdvancedSearch/Properties/launchSettings.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AspNetCore/React/AdvancedSearch/Properties/launchSettings.json b/AspNetCore/React/AdvancedSearch/Properties/launchSettings.json index d97e6c3f..85c90d61 100644 --- a/AspNetCore/React/AdvancedSearch/Properties/launchSettings.json +++ b/AspNetCore/React/AdvancedSearch/Properties/launchSettings.json @@ -12,7 +12,8 @@ "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" } }, "EqReactDemo": { @@ -20,7 +21,8 @@ "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" } } }