dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.55k stars 10.05k forks source link

ASP.NET Core with Angular throws Exception on startup after publish #55687

Open kratofl opened 6 months ago

kratofl commented 6 months ago

Is there an existing issue for this?

Describe the bug

When I publish my ASP.NET Core application with angular I get an exception:

Expected Behavior

I should not throw an exception on startup.

Steps To Reproduce

  1. Create and project with the template: "Angular and ASP.NET Core" image
  2. Start it once to test
  3. Publish to local folder image
  4. Start the .exe file.

Exceptions (if any)

warn: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[16] The WebRootPath was not found: C:\source\repos\AngularApp1\AngularApp1.Server\bin\Release\net8.0\wwwroot. Static files may be unavailable. warn: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[16] The WebRootPath was not found: C:\source\repos\AngularApp1\AngularApp1.Server\bin\Release\net8.0\wwwroot. Static files may be unavailable. fail: Microsoft.Extensions.Hosting.Internal.Host[11] Hosting failed to start System.IO.IOException: Failed to bind to address http://[::]:8080: address already in use. ---> Microsoft.AspNetCore.Connections.AddressInUseException: Only one usage of each socket address (protocol/network address/port) is normally permitted. ---> System.Net.Sockets.SocketException (10048): Only one usage of each socket address (protocol/network address/port) is normally permitted. at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName) at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Bind(EndPoint localEP) at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint) at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind() --- End of inner exception stack trace --- at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind() at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>cDisplayClass28_01.<<StartAsync>g__OnBind|0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken) --- End of inner exception stack trace --- at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.AnyIPListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(ListenOptions[] listenOptions, AddressBindContext context, Func2 useHttps, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication1 application, CancellationToken cancellationToken) at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token) at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List1 exceptions, Func3 operation) Unhandled exception. System.IO.IOException: Failed to bind to address http://[::]:8080: address already in use. ---> Microsoft.AspNetCore.Connections.AddressInUseException: Only one usage of each socket address (protocol/network address/port) is normally permitted. ---> System.Net.Sockets.SocketException (10048): Only one usage of each socket address (protocol/network address/port) is normally permitted. at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName) at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Bind(EndPoint localEP) at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint) at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind() --- End of inner exception stack trace --- at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind() at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>cDisplayClass28_01.<<StartAsync>g__OnBind|0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken) --- End of inner exception stack trace --- at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.AnyIPListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(ListenOptions[] listenOptions, AddressBindContext context, Func2 useHttps, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication1 application, CancellationToken cancellationToken) at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token) at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List1 exceptions, Func3 operation) at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token) at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host) at Program.

$(String[] args) in C:\source\repos\AngularApp1\AngularApp1.Server\Program.cs:line 30

.NET Version

8.0.204

Anything else?

  • Visual Studio 2022 Windows: 17.9.6
  • Node: v20.12.2
  • Angular: 17.3.0
  • I've triple checked it, nothing is running on port 8080 already
amcasey commented 6 months ago

That error appears to say that the port is already in use. You shut down the instance you ran to test the project in step (2) before running another copy in step (4)?

Edit: just noticed the note at the bottom of your report. How are you confirming that 8080 is available?

amcasey commented 6 months ago

@danroth27 Is that template owned by aspnetcore or JavaScript/TypeScript?

amcasey commented 6 months ago

I did my best to follow those repro steps and didn't see that behavior. It wasn't specified, but I assumed it was the AngularApp1.Server project that I should publish to a local folder. I'm a little curious how yours ended up on port 8080 - launchSettings.json appears to specify a random port and running the exe directly appears to default to 5000.

danroth27 commented 6 months ago

@danroth27 Is that template owned by aspnetcore or JavaScript/TypeScript?

This template is owned by @joj's team, but we work pretty closely with them on it.

kratofl commented 6 months ago

I did my best to follow those repro steps and didn't see that behavior. It wasn't specified, but I assumed it was the AngularApp1.Server project that I should publish to a local folder. I'm a little curious how yours ended up on port 8080 - launchSettings.json appears to specify a random port and running the exe directly appears to default to 5000.

Ok. I've sort of "fixed" it. I forgot that I set the environment variable (ASPNETCORE_URLS), but it was a while ago.

I've removed that entry and now it works. But I'm still curious why it didn't work.

kratofl commented 6 months ago

But theres another issue. The compiled angular code is put into "wwwroot/browser". And I cannot access the page. But when I move the compiled angular files out of the browser folder directly into wwwroot, then it works. Any idea why?

amcasey commented 6 months ago

But I'm still curious why it didn't work [with ASPNETCORE_URLS].

Presumably, another process was consuming the same environment variable. You could check using something like netstat.

But theres another issue.

Can you please provide new repro steps? Are you using the built app or the published one? How did you try to access the page? What behavior did you see when you failed to access it?

kratofl commented 6 months ago

Sorry for the late response

Can you please provide new repro steps? Are you using the built app or the published one? How did you try to access the page? What behavior did you see when you failed to access it?

I use the published one, the build one works fine with the debugger. I've tried to access it with "localhost:5000". It just says that the page isn't working. But after moving the content from the sub directory to wwwroot directly it works fine.

amcasey commented 5 months ago

I've tried to access it with "localhost:5000".

I'm assuming you mean in a browser, as opposed to curl or HttpClient?

It just says that the page isn't working.

Is it the client or the server saying that? What does the server log show?

kratofl commented 5 months ago

I'm assuming you mean in a browser, as opposed to curl or HttpClient?

Yes I used my browser.

Is it the client or the server saying that? What does the server log show?

My browser showed me that. When my application starts I also get this message: The WebRootPath was not found: C:\source\repos\AngularApp1\AngularApp1.Server\bin\Release\net8.0\wwwroot. Static files may be unavailable. It tries to find the compiled angular files in the wwwroot folder directly but they are located in a subfolder browser. Everytime I do a publish, I need to move the files from wwwroot/browser to wwwroot

amcasey commented 5 months ago

Sounds like it could be this: https://github.com/dotnet/sdk/issues/6597

Edit: or maybe not quite? It sounds like you're saying the files are published, but in a subfolder?

joj commented 5 months ago

Hi! By default we will take the assets (javascript files and other things you may need to publish) from wwwroot, but you can change that but configuring a property in the Angular project. I believe what you're saying is you want the assets to be in wwwroot/browser? I think this (or something similar to this) should work:

<PropertyGroup>
  <PublishAssetsDirectory>wwwroot/browser</PublishAssetsDirectory>
</PropertyGroup>

That path may need to be rooted, I can't recall right now, so that may work too.

If on the other hand you want to change where angular is putting the files (so wwwroot instead of wwwroot/browser), that's angular configuration that should be in a js file in your project.

kratofl commented 5 months ago

If on the other hand you want to change where angular is putting the files (so wwwroot instead of wwwroot/browser), that's angular configuration that should be in a js file in your project.

I want that yes, but I haven't found the configuration for it. But the main issue about that is that the published project that has been freshly created is not working on the IIS because the angular files are located (by default) in wwwroot/browser and I have to move the files every time to wwwroot in order for it to work.

kratofl commented 1 day ago

I've found a workaround.

I've added this configuration to my .csproj file

<Target Name="MoveAngularFiles" AfterTargets="Publish">
  <ItemGroup>
    <AngularFiles Include="$(PublishDir)wwwroot\browser\**\*" />
  </ItemGroup>

  <!-- Move files from wwwroot/browser to wwwroot -->
  <Copy SourceFiles="@(AngularFiles)"
        DestinationFiles="@(AngularFiles->'$(PublishDir)wwwroot\%(RecursiveDir)%(Filename)%(Extension)')"
        SkipUnchangedFiles="true" />

  <!-- Delete wwwroot/browser -->
  <RemoveDir Directories="$(PublishDir)wwwroot\browser" />
</Target>

The reason why I cannot access the web without moving the files from wwwroot/browser to wwwroot is that the IIS is looking for an Index.html file in the wwwroot folder but is not finding any. But this also applies if I start the .exe manually. It cannot find the browser/index.html since the application is not configured this way.

It's strange that this is an issue when you use the template from ASP.NET Core but no one has addressed it. I've seen another repo which is doing it this way.