Search code examples
.net-core.net-6.0self-containedaspnetcore-environment

How to prevent a 404 when running my .NET 6 Web API as a self contained service?


I have made a simple .NET 6 Web API. This works fine when running from Visual Studio (by just pressing F5). However, when running my application as a self-contained service I get a 404 for calls that are supposed to succeed. How can I make sure my self-contained service starts behaving the same?

Here is my Program.json

using Microsoft.Extensions.Hosting.WindowsServices;


var options = new WebApplicationOptions
{
    Args = args,
    ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};

var builder = WebApplication.CreateBuilder(options);
builder.Services.AddRazorPages();

builder.Host.UseWindowsService();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapControllers();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); });


await app.RunAsync();

Here is my csproj


<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
      <ImplicitUsings>enable</ImplicitUsings>
      <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
      <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <UserSecretsId>XXXXX-78E75397-5A01-4397-9481-E423B4BF54C2</UserSecretsId>
  </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
        <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
    </ItemGroup>
</Project>

This is how I test:

  1. Open the solution file and press F5.
  2. Run dotnet publish --no-build -c Release executed from the folder with the csproj in it. This generates an exe in side XXXXX\bin\Release\net6.0\win-x64 so I run the exe from this folder by going to this folder from the command prompt and then run the exe.

In both situations, I test using curl -I http://localhost:5000/swagger/index.html .

This returns a 404 when doing option 2 (with dotnet publish). When running from visual studio, I just get a 200.

How to make sure the result of dotnet publish behaves the same as my application behaves when running from Visual Studio?


Solution

  • The service is self-hosted in both cases. The other option would be to use IIS.

    The reason Swagger is disabled is because the code explicitly enables it only in a development environment.

    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    

    The environment is controlled by the value of the ASPNETCORE_ENVIRONMENT environment variable. Unless you explicitly set that environment variable in a user's or system's Environment Variables, it won't exist, and your service will run in Production mode.

    Shell scripts often set environment variables before executing programs that depend on them. Visual Studio sets the environment variables specified in launchsettings.json before launching your program. If you try to run the program outside VS you'll have to set them yourself, eg:

    SET ASPNETCORE_ENVIRONMENT=Development
    
    myservice.exe
    

    launchsettings.json is used to specify launch profiles, not just environment variables. This includes the hosting model, command line arguments and more. Typically it's used by an IDE like Visual Studio but you can also launch a .NET program with dotnet run using a specific profile with the --launch-profile <NAME> option

    The docs explain that this file isn't deployed, so it never appears in the publish folder:

    The launchSettings.json file:

    • Is only used on the local development machine.
    • Is not deployed.
    • Contains profile settings.