TimeoutException: The Angular CLI process did not start listening for requests within the timeout period of 0 seconds

前端 未结 11 1937
天命终不由人
天命终不由人 2020-12-09 15:25

I\'m getting this error after upgrading to angular 9. I\'m using visual studio 2019, ASP .NET core with angular. Even if I create new project and update angular to 9 version

相关标签:
11条回答
  • 2020-12-09 16:14

    TL;DR

    Sadly the issue seems related to some changes in the way Angular CLI starts up the angular part of the application. As per this issue:

    https://github.com/dotnet/aspnetcore/issues/17277

    proposed solutions are to set progress: true in angular.json or perform a simple echo prior to ng serve (https://github.com/dotnet/aspnetcore/issues/17277#issuecomment-562433864).

    Full answer

    I dug the asp.net core code base (https://github.com/dotnet/aspnetcore), looking how the Angular template starts up the Angular application.

    The core engine that starts up the angular server is represented by two classes: AngularCliMiddleware (https://git.io/JvlaL) and NodeScriptRunner (https://git.io/Jvlaq).

    In AngularCliMiddleware we find this code (I removed the original comments and added some of my own to explain a couple of things):

    public static void Attach(ISpaBuilder spaBuilder, string npmScriptName)
    {
        var sourcePath = spaBuilder.Options.SourcePath;
        if (string.IsNullOrEmpty(sourcePath))
        {
            throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
        }
    
        if (string.IsNullOrEmpty(npmScriptName))
        {
            throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
        }
    
        // Start Angular CLI and attach to middleware pipeline
        var appBuilder = spaBuilder.ApplicationBuilder;
        var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
        var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger);
    
        var targetUriTask = angularCliServerInfoTask.ContinueWith(
            task => new UriBuilder("http", "localhost", task.Result.Port).Uri);
    
        SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
        {
            var timeout = spaBuilder.Options.StartupTimeout;
            return targetUriTask.WithTimeout(timeout,
                $"The Angular CLI process did not start listening for requests " +
    
                // === NOTE THIS LINE, THAT CARRIES THE "0 seconds" BUG!!!
                $"within the timeout period of {timeout.Seconds} seconds. " + 
    
                $"Check the log output for error information.");
        });
    }
    
    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);
        npmScriptRunner.AttachToLogger(logger);
    
        Match openBrowserLine;
        using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
        {
            try
            {
                // THIS LINE: awaits for the angular server to output
                // the 'open your browser...' string to stdout stream
                openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch(
                    new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout));
            }
            catch (EndOfStreamException ex)
            {
                throw new InvalidOperationException(
                    $"The NPM script '{npmScriptName}' exited without indicating that the " +
                    $"Angular CLI was listening for requests. The error output was: " +
                    $"{stdErrReader.ReadAsString()}", ex);
            }
        }
    
        var uri = new Uri(openBrowserLine.Groups[1].Value);
        var serverInfo = new AngularCliServerInfo { Port = uri.Port };
    
        await WaitForAngularCliServerToAcceptRequests(uri);
    
        return serverInfo;
    }
    

    As you can see, the StartAngularCliServerAsync method creates a new NpmScriptRunner object, that is a wrapper around a Process.Start method call, basically, attaches the logger and then waits for the StdOut of the process to emit something that matches "open your browser on httpSOMETHING...".

    Fun thing is this should work!

    If you run ng serve (or npm run start) in ClientApp folder, once the server starts it still emit the output "open your browser on http...".

    If you dotnet run the application, the node server actually starts, just enable all the logs in Debug mode, find the "Starting @angular/cli on port ..." line and try visiting localhost on that port, you'll see that your angular application IS running.

    Problem is that for some reason the StdOut is not getting the "open your browser on" line anymore, nor it is written by the logger... it seems that in some way that particular output line from ng serve is held back, like it's no longer sent in the Stardard Output stream. The WaitForMatch method hits his timeout after 5 seconds and is catched from the code of the WithTimeout extension method, that outputs the (bugged) "... 0 seconds ..." message.

    For what I could see, once you dotnet run your application, a series of processes is spawn in sequence, but i couldn't notice any difference in the command lines from Angular 8 to Angular 9.

    My theory is that something has been changed in Angular CLI that prevents that line to be sent in stdout, so the .net proxy doesn't catch it and can't detect when the angular server is started.

    As per this issue:

    https://github.com/dotnet/aspnetcore/issues/17277

    proposed solutions are to set progress: true in angular.json or perform a simple echo prior to ng serve (https://github.com/dotnet/aspnetcore/issues/17277#issuecomment-562433864).

    0 讨论(0)
  • 2020-12-09 16:18

    I resolved it by changing:

    "scripts": {   
            "start": "ng serve",
    

    to:

     "scripts": {   
            "start": "echo Starting... && ng serve",
    

    in package.json

    0 讨论(0)
  • 2020-12-09 16:23

    In package.json change ng serve to ng serve --host 0.0.0.0

    0 讨论(0)
  • 2020-12-09 16:25

    Here is a workaround:

    • In package.json change the start-script from "ng serve" to "ngserve"
    "scripts": {
      "start": "ngserve",
    
    • In the same directory create a file ngserve.cmd with the following content:
    @echo ** Angular Live Development Server is listening on localhost:%~2, open your browser on http://localhost:%~2/ **
    ng serve %1 %~2
    

    Now Dotnet gets the line it is waiting for. After that the command ng serve will start the server (so in fact the Angular Live Development Server is not yet listening), the browser will open, and first it won't work (ng serve still compiling), but if you press reload after a while, it should be fine.

    This is just a workaround, but it works for us.

    0 讨论(0)
  • 2020-12-09 16:27

    Here is what I did

    from Fairlie Agile, commented out this line in main.ts

    export { renderModule, renderModuleFactory } from '@angular/platform-server';
    

    From Claudio Valerio In angular.json , set

    "progress": true,
    

    Now I can run the app by clicking on F5 / Run IIS Express

    0 讨论(0)
提交回复
热议问题