angular / angular-cli

CLI tool for Angular
https://cli.angular.dev
MIT License
26.79k stars 11.98k forks source link

Issue moving to browser-esbuild from browser (Angular 17) #28360

Closed awdorrin closed 2 months ago

awdorrin commented 2 months ago

Command

serve

Is this a regression?

The previous version in which this bug was not present was

No response

Description

We have an application built with ASP.Net Core and Angular using the legacy SpaServices / ClientApp template. Over time we have upgraded to .net 8 and Angular 17. I wanted to try to new build system, so I followed the instructions to replace "builder": "@angular-devkit/build-angular:browser", with "builder": "@angular-devkit/build-angular:browser-esbuild",

Upon launching from Visual Studio, using the IISExpress option, I see the following, and the angular app never loads in the browser.

Microsoft.Hosting.Lifetime: Information: Application started. Press Ctrl+C to shut down.
Microsoft.Hosting.Lifetime: Information: Hosting environment: Development
Microsoft.Hosting.Lifetime: Information: Content root path: C:\work\_git\MyApp\MyApp\MyApp
Microsoft.AspNetCore.SpaServices: Error: The 'vendorChunk' option is not used by this builder and will be ignored.

Microsoft.AspNetCore.SpaServices: Error: - Building...

Microsoft.AspNetCore.SpaServices: Error: ΓùÅ [DEBUG] Failed to read directory "C:\\work\\_git\\MyApp\\MyApp\\MyApp\\ClientApp\\angular:script\\global:scripts":
 open C:\work\_git\MyApp\MyApp\MyApp\ClientApp\angular:script: The system cannot find the file specified.

If I check the Chrome DevTools Network tab, I see the following exchange:

authorization.oath2?client_id=... GET  302
authorization.ping                GET  200
signin-oidc                       POST 302        https://localhost:44317/signin-oidc
localhost                         GET  (pending)  https://localhost:44317/

It hangs indefinitely at that point. If I switch back to 'browser'
I get the same flow, except instead of hanging indefinitely, it takes about 50 seconds (Angular build time) the Get completes and provides the index.html and starts loading the app resources.

As I write this up, I am starting to think that the error about not finding the file specified is a red herring. Perhaps there is a difference between the output from browser and browser-esbuild, and that whatever the SpaServices is looking for, that was written out from browser, is not being written out by browser-esbuild, so SpaServices is still waiting for something before it will proxy (or whatever) and return index.html (ie. still waiting for Angular to startup on localhost:4200)

Minimal Reproduction

see above

Exception or Error

No response

Your Environment

Angular CLI: 17.3.8
Node: 18.20.3
Package Manager: npm 10.7.0
OS: win32 x64

Angular: 17.3.11
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.1703.8
@angular-devkit/build-angular     17.3.8
@angular-devkit/core              17.3.8
@angular-devkit/schematics        17.3.8
@angular/cdk                      17.3.10
@angular/cli                      17.3.8
@angular/material                 17.3.10
@angular/material-luxon-adapter   17.3.10
@schematics/angular               17.3.8
rxjs                              7.8.1
typescript                        5.2.2
zone.js                           0.14.7

Anything else relevant?

No response

awdorrin commented 2 months ago

It looks like this is caused by the difference in browser and browser-esbuild output when serving.

browser output:

Microsoft.AspNetCore.SpaServices: Information: Build at: 2024-09-06T14:41:30.035Z - Hash: fd9b231bdc522a52 - Time: 43228ms
Microsoft.AspNetCore.SpaServices: Information: ** Angular Live Development Server is listening on 127.0.0.1:64672, open your browser on http://127.0.0.1:64672/ **
Microsoft.AspNetCore.SpaServices: Information: √ Compiled successfully.

while browser-esbuild does:

Microsoft.AspNetCore.SpaServices: Information: Application bundle generation complete. [22.755 seconds]
Microsoft.AspNetCore.SpaServices: Information: Watch mode enabled. Watching for file changes...
Microsoft.AspNetCore.SpaServices: Information:   Γ₧£  Local:   http://127.0.0.1:64954/

The 'legacy' SpaServices.Exetensions for AngularCli however, is specifically looking for:

openBrowserLine = await scriptRunner.StdOut.WaitForMatch(
  new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout));

See: AngularCliMiddleware.cs

awdorrin commented 2 months ago

Before someone responds saying that SpaServices.Extensions are deprecated, you should be aware that the newer SpaProxy client-side proxying is broken when it comes to .Net back-end Authentication middleware.

OAuth/OIDC Authentication middleware in redirects to the authentication server and the sequence ends up doing a post to /signin-oidc. The problem being that the existing middleware provides a Redirect URI using the back-end server port (say https://locahost:44300) while the browser is loading the angular app from the SPA/Client-Side server (say https://localhost:4200) Authentication Servers don't typically support cross-domain requests, so the browsers kill the redirect for the authentication requests, since 4200 and 44300 are different domains.

From what I can tell, Microsoft's decision to move from SpaServices.Extensions to SpaProxy, while making things easier for them, breaks their own Authentication mechanism.

I have started experimenting with AspNetCore.SpaYarp which looks like it might be a better solution that SpaProxy.

alan-agius4 commented 2 months ago

It seems that there is an issue in their issue tracking tracking this problem https://github.com/dotnet/aspnetcore/issues/53307

There is a couple of workarounds and also a suggestion to fix it on their side by updating the regular expression.

awdorrin commented 2 months ago

Ah, thanks for the link - I hadn't found that page. I just tried the last suggestion from that thread, of calling npm start and then using the UseProxyToSpaDevelopmentServer call

It does work, with the drawback is you get an exception page displayed at first, and then have to manually refresh the page once the build from ng serve completes.

I'll have to see if I can refine that to make it a bit more user friendly.

Thanks!

angular-automatic-lock-bot[bot] commented 1 month ago

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.