A monthly overview of things you need to know as an architect or aspiring architect.
View an example
QCon San Francisco (Nov 18-22): Get assurance you’re adopting the right software practices.
Register Now
Applying AI to the SDLC: New Ideas and Gotchas! - Leveraging AI to Improve Software Engineering
Tracy Bannon discusses using Generative AI in software engineering with AI-assistance to meet the speed and quality of end-users demand.
Efficient DevSecOps Workflows With a Little Help From AI
Michael Friedrich is exploring how teams face varying levels of inefficiency in their DevSecOps processes, hindering progress and innovation. He highlights common issues like excessive debugging time and inefficient workflows, while also demonstrating how Artificial Intelligence (AI) can be a powerful tool to streamline these processes and boost efficiency.
Key Takeaways
In .NET 6, the communication between front-end SPA and back-end .NET API was changed.
From .NET 6, the template uses the front end’s proxy solutions to send the request to the back end, resulting in a more independent back end.
A proxy for the development servers enables readable and debuggable code for both the front and back end.
Using Microsoft’s reverse proxy solution Yarp with the SpaYarp package is still a viable alternative.
The .NET 6 changes also apply to the following versions.
ASP.NET has many templates that help you get started with development on the .NET ecosystem. The templates offer Server Side Rendering (Razor Pages, MVC, Blazor Server) and Client Side Rendering (Blazor WebAssembly) solutions, as well as Single Page Applications (Angular, React).
This article will describe the latter one, the
SPA templates
, and the proxy changes introduced for the development environment from .NET 5 to .NET 6, .NET 7, and on to the next versions.
.NET 5 support was retired May 10, 2022. A breaking change that switches the way proxies are used in the ASP.NET SPA template was introduced in .NET 6 Preview 4, May 25, 2021. Users were left frustrated without a clear way to upgrade to the new proxy approach.
As this change mainly affected smaller projects and enthusiasts, the only answers were scattered in forums (few examples
1
,
2
,
3
) with good hints and suggestions but no clear guide. A clear migration guide only appeared in November 2022 after .NET 7 was made available.
The new template changes how the communication between the front-end SPA and the back-end .NET API is done. The old templates (from .NET Core to .NET 5) used a specialized middleware that launched the development server for the front-end framework and then proxied the request from the .NET server to the front-end Node.js.
The ASP.NET SPA templates are compiled and launched differently for development and the published version.
In the final released version, Kestrel serves the requests, while for development, Node.js and Kestrel are both used, so the front-end and back-end code can be debugged more easily. The changes affect only the development environment.
[Click on the image to view full-size]
This approach meant that the launch code needed to be specific for each front-end framework, resulting in hard-to-maintain code for each front-end framework that the Microsoft team wanted to support.
[Click on the image to view full-size]
From .NET 6, the new templates for Angular and React switch how the front end and back end communicate. They use the front end’s proxy solutions to send the request to the back end. The popular front-end frameworks already have built-in support for development server proxying, but they must also be configured each specific to the used framework. The ASP.NET app still launches the front-end development server, but the request comes from that server.
Advantages of this new approach include:
Simpler configuration in the back-end files
The back end is more independent from the front-end framework but not completely separate as the launch command and URL are still specific
No more framework-specific code in the back end’s Startup.cs or Program.cs files
Extensible to other front-end frameworks not included in the templates
The logic for starting the front-end development server during development is done using Microsoft.AspNetCore.SpaProxy package and setting the front end’s URL and launch command.
<SpaProxyServerUrl>https://localhost:44435</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
For the front-end side, each framework has its own implementation. For example, React uses a third-party package
http-proxy-middleware
, which is like the following
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
Angular has instead a more extensive configuration solution. They use a file named
proxy.conf.json
, where developers can define the proxy and then add it to the options in
angular.json
.
proxy.conf.json
"/api": {
"target": "http://localhost:3000",
"secure": false
angular.json
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
One great advantage of this template is that the application is a single project: the final published version has one entry point, and the front-end code is served as static files from the ASP.NET process. This makes the deployment simpler because it is only one service that needs to be deployed and maintained. It is great for small solutions, side projects, or just trying something quickly.
The template comes with:
Already built pages that contain client-side navigation
Development server integration
Efficient production builds
Counter and fetch-data examples
Depending on the team size, it can also work for bigger projects, as under the ClientApp folder, one can work as if it were a separate front end only solution. But creating a totally separate front end is much cleaner in the long run. Creating well-defined projects help with maintaining the code. This also helps specialized developers (like React or .NET) work only with the projects they know.
Using a proxy during development has multiple benefits for the developer:
Forwarding requests to a specific path, like
awesomeweb.com/about
to
localhost:3210/about
Working with relative paths
Rewriting paths
Resolving HTTP/HTTPS issues that could arise due to certificates
Solving CORS (Cross-Origin Resource Sharing) errors, without adding explicit allow rules for local devServers
Both front-end and back-end code is readable and can be debugged
These benefits can greatly accelerate the development process and make the developers experience with asp.net more pleasant.
Suppose you don’t like using the front-end-proxy-to-back-end approach because there is a difference when serving pages in development and production, or you are not a fan of setting up the front-end proxying. In that case, an alternative is available using Microsoft’s reverse proxy solution Yarp. The package is called
SpaYarp
, and the approach is a mix of using the new template configurations for the back end without flipping the proxy communication.
If you want to upgrade your existing solution to .NET 6 (LTS) or later, then the following steps will summarize what files need to be updated both on the .NET side and the Angular (annotated with
a
) or React (annotated with
r
) side.
Migration
To migrate an ASP.NET Angular or React template, the following files need to be updated:
Back end:
the project file
LaunchSettings.json
Startup.cs
or
Program.cs
(depending on the setup)
Front end:
●
package.json
●
angular.json
(angular only)
● files must be added to set up the proxy and HTTPS
Back end
1.
In the Nuget packages section of your project, remove the reference to
Microsoft.AspNetCore.SpaServices.Extensions
and add the new package called
Microsoft.AspNetCore.SpaProxy
.
2.
In the
csproj
file of the project, add in the first
PropertyGroup
<SpaProxyServerUrl>https://localhost:<<Insert Frontend Port>></SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
At the end, there is a Target with a name of
PublishRunWebpack
. In that group, update the
ResolvedFileToPublish->RelativePath
to
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
3.
In the
launchSettings.json
, update every profile with the property:
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
It should look similar to the following lines:
"profiles": {
"TestProject": {
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
"IIS Express": {
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
4.
In the
Startup.cs
or
Program.cs
you need to remove:
●
AddSpaStaticFiles()
:
services.AddSpaStaticFiles(configuration =>
... //All the configs/code inside
●
UseSpaStaticFiles()
:
app.UseSpaStaticFiles();
●
UseSpa
:
app.UseSpa(spa =>
... //All the configs/code inside
5.
You should add a fallback to "
index.html
" in the endpoints section, like:
app.UseEndpoints(endpoints => {
endpoints.MapFallbackToFile("index.html");
Front end
Even if, for the front end part of the templates, Angular and React have different implementations, they both use the same HTTPS setup file.
1.
Add
aspnetcore-https.js
next to the
package.json
file
// This script sets up HTTPS for the application using the ASP.NET Core HTTPS certificate
const fs = require('fs');
const spawn = require('child_process').spawn;
const path = require('path');
const baseFolder =
process.env.APPDATA !== undefined && process.env.APPDATA !== ''
? `${process.env.APPDATA}/ASP.NET/https`
: `${process.env.HOME}/.aspnet/https`;
const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
if (!certificateName) {
console.error('Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<<app>> explicitly.')
process.exit(-1);
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
spawn('dotnet', [
'dev-certs',
'https',
'--export-path',
certFilePath,
'--format',
'Pem',
'--no-password',
], { stdio: 'inherit', })
.on('exit', (code) => process.exit(code));
For the Angular template:
2a.
Update
package.json
"start": "run-script-os",
"start:windows": "ng serve --port <<Insert Frontend Port>> --ssl --ssl-cert %APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem --ssl-key %APPDATA%\\ASP.NET\\https\\%npm_package_name%.key",
"start:default": "ng serve --port <<Insert Frontend Port>> --ssl --ssl-cert $HOME/.aspnet/https/${npm_package_name}.pem --ssl-key $HOME/.aspnet/https/${npm_package_name}.key",
Note: use the same port as in the back-end files.
3a.
Install
run-script-os
with the command
npm install --save-dev run-script-os
This package adds the ability to use OS-specific operations in npm scripts as you can see above with the
:windows
denotation.
4a.
In the
angular.json
file for development, add a
proxyConfig
property pointing to the new proxy config file:
"serve": {
"configurations": {
"development": {
"proxyConfig": "proxy.conf.js"
5a.
Add the
proxy.conf.js
file with the contents:
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:[IIS-HTTP-PORT]';
const PROXY_CONFIG = [
context: [
"/weatherforecast",
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
module.exports = PROXY_CONFIG;
For the react template:
2r.
Update the
package.json
"prestart": "node aspnetcore-https && node aspnetcore-react",
"start": "rimraf ./build && react-scripts start",
3r.
Add
.env.development
environment file:
PORT=<<Insert Frontend Port>>
HTTPS=true
Note: use the same port as in the back-end files.
4r.
Add the file
aspnetcore-react.js
// This script configures the .env.development.local file with additional environment variables to configure HTTPS using the ASP.NET Core
// development certificate in the webpack development proxy.
const fs = require('fs');
const path = require('path');
const baseFolder =
process.env.APPDATA !== undefined && process.env.APPDATA !== ''
? `${process.env.APPDATA}/ASP.NET/https`
: `${process.env.HOME}/.aspnet/https`;
const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
if (!certificateName) {
console.error('Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<<app>> explicitly.')
process.exit(-1);
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
if (!fs.existsSync('.env.development.local')) {
fs.writeFileSync(
'.env.development.local',
`SSL_CRT_FILE=${certFilePath}
SSL_KEY_FILE=${keyFilePath}`
} else {
let lines = fs.readFileSync('.env.development.local')
.toString()
.split('\n');
let hasCert, hasCertKey = false;
for (const line of lines) {
if (/SSL_CRT_FILE=.*/i.test(line)) {
hasCert = true;
if (/SSL_KEY_FILE=.*/i.test(line)) {
hasCertKey = true;
if (!hasCert) {
fs.appendFileSync(
'.env.development.local',
`\nSSL_CRT_FILE=${certFilePath}`
if (!hasCertKey) {
fs.appendFileSync(
'.env.development.local',
`\nSSL_KEY_FILE=${keyFilePath}`
5r.
Finally, in the
ClientApp\src
folder add a
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:[IIS-HTTP-PORT]';
const context = [
"/weatherforecast",
module.exports = function(app) {
const appProxy = createProxyMiddleware(context, {
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
app.use(appProxy);
After all the changes, build and run the application. Both Angular and React versions should follow the new proxy strategy and have HTTPS support.
Conclusion
This new approach of using the proxies in the templates looks more robust and will probably remain in use for the next releases of the ASP.NET SPA templates.
The ease of having a starting command for the front end defined in the SpaProxyLaunchCommand and a URL linking to the front-end framework
SpaProxyServerUrl
makes it simpler to use other front-end frameworks than the ones available from Microsoft.
The example connections for React and Angular show possible scenarios to leverage the front end’s proxy to send requests to the back end while using it in debug mode. With this solution, you can deploy a SPA application with a front end on the same machine/instance because it is only one process, providing a quick and easy way to start development on an app with SPA as a front end.
The InfoQ
Newsletter
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers.
View an example
The InfoQ
Newsletter
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers.
View an example
Get a quick overview of content published on a variety of innovator and early adopter technologies
Learn what you don’t know that you don’t know
Stay up to date with the latest information from the topics you are interested in
InfoQ Dev Summit Munich
September 26-27, 2024
InfoQ Dev Summit Munich is a two-day software conference featuring 22 technical talks sharing actionable insights on Generative AI, security, modern web apps, and more.
Learn from senior developers facing the same challenges as you as they share proven tactics, not just trends, empowering you to make smart, focused choices for your immediate dev roadmap.
Register Now