How does spa.UseAngularCliServer() middleware serve a webpage?
There is no need for a dist folder as the files are built and held in-memory by webpack dev server which is used internally by Angular CLI.
The requests for the SPA are reverse proxied to the Angular Dev Server by IIS. So IIS receives a request from the browser --> IIS sends a request to angular dev server --> dev server sends data back to IIS --> IIS sends data to browser.
Although that's done by proxying requests to the Angular Development Server
, the proxying is not proxied by IIS , but by ASP.NET Core itself . IIS has no idea about the port listened by Angular Dev Server .
UseAngularCliServer("start")
will firstly invoke the commandnpm start -- --port {dynamic_port}
The {dynamic_port}
here is computed at RunTime
.Since the npm
command has been already configured in package.json
, it will then start a dev server byng serve -- --port {dynamic_port}
.
- if the
ng serve
run flawlessly , it will create a plainHttpClient
for proxy and use it to perform proxy (forward requests toAngular Dev Server
with the plainHttpClient
, and copy the response as it's own response) . TheWebSocket
proxy will take place in a similar way .
You can find the source code on GitHub . It's easy to read and understand .
Internally , the UseAngularCliServer(npmScript: "start")
will invoke AngularCliMiddleware
to do step 1
and step 2
. The step 1 is done by :
var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger);
The StartAngularCliServerAsync
method will find an available TCP port , and run command npm start -- --port {port}
to start ng serve
:
private static async Task<AngularCliServerInfo> StartAngularCliServerAsync(
string sourcePath, string npmScriptName, ILogger logger)
{
var portNumber = TcpPortFinder.FindAvailablePort();
logger.LogInformation($"Starting @angular/cli on port {portNumber}...");
var npmScriptRunner = new NpmScriptRunner( sourcePath, npmScriptName, $"--port {portNumber}", null);
// ...
}
See the complete code here
And the Step 2 is done by :
public static void UseProxyToSpaDevelopmentServer(this ISpaBuilder spaBuilder, Func<Task<Uri>> baseUriTaskFactory)
{
// ...
// Proxy all requests to the SPA development server
applicationBuilder.Use(async (context, next) =>
{
var didProxyRequest = await SpaProxy.PerformProxyRequest( context, neverTimeOutHttpClient, baseUriTaskFactory(), applicationStoppingToken, proxy404s: true);
});
}
The implementation of PerfromProxyRequest()
can be found here . For normal requests , It simply forward them using a plain HttpClient
and copy the response as its own response .
Back to your question :
Where is the dist folder?
Since you have configured a spa.UseAngularCliServer("start")
:
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
All those requests will be processed by local dev server launched by angular cli. As a result , there's no dist
on disk at all . The same will take place if you run the command ng serve
manually .
How does "ng serve" relate to IIS Express?
IIS has no idea about the port listened by Angular Dev Server. When there's an incoming message , IIS just sends it to your ASP.NET Core server . If the request cannot be processed by staticFiles , MVC , or any other middlewares that you've configured before UseSpa()
, ASP.NET Core will forward it to the Dev Server .