App Configuration affecting VS Web Server Hosting

28 Views Asked by At

In my ASP.NET API im having an issue for a while where i cannot host my web app locally using VS web server on ports other than the default ones (i.e. http: 5000, https: 5001). VS keeps on waiting for web server to listen on the ports specified in my LaunchProfile.json indefinitely while the app is being hosted on the default ports.

{
  "profiles": {
    "HHT_API": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "https://localhost:7291;http://localhost:7290"
    }
  }
}

What does this have to do with ConfigurationBuilder?
Well, I was having an argument with my colleague regarding how Configuration should be provided in DI. He relies on DI to auto-provide the configuration, while I do it explicitly through creating an IConfiguration object from ConfigurationBuilder

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", false, true)
    .Build();

builder.Services.AddSingleton(configuration);

after deciding to try it his way, i realized that the issue is gone, i can now host the app using any set of ports unlike before.

What am i missing here?

EDIT 1: to clarify, below is program.cs for both cases:

Case 1: Creating an IConfiguration object with Configuration builder and passing it as a DI service (app is hosted on default ports only)

using HHT_API.Databases.HHTCustom;
using HHT_API.Modules.HHT.Models.ResponseModels;
using HHT_API.Modules.Logger.Models;
using HHT_API.Modules.Middleware.API;
using HHT_API.WrapperManagement;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.EntityFrameworkCore;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllersWithViews()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
    })
    .ConfigureApiBehaviorOptions(options =>
    {
        //configure a special response for invalid model state
        options.InvalidModelStateResponseFactory = context =>{
            ModelStateDictionary modelStatePtr = context.ModelState;  
            modelStatePtr.Remove(modelStatePtr.ElementAt(0).Key); //remove the key of the class model itself
            
            List<string> invalidFields = modelStatePtr.Keys.Select(x => x.Replace("$", "").Replace(".", "")).ToList();
            
            var result = new OkObjectResult(new MainResponseModel<object>() {
                MessageCode = (int)StatusCode.ModelValidationError,
                MessageText = $"Invalid entry for ({string.Join(", ", invalidFields)}). Please check your input and try again!",
                Data = null
            });
            return result;
        };
    });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddHsts(options =>
{
    options.MaxAge = TimeSpan.FromSeconds(31536000);
    options.IncludeSubDomains = true;
    options.Preload = true;
});
builder.Services.AddHttpsRedirection(options =>
{
    options.HttpsPort = 443;
    options.RedirectStatusCode = 308;
});

builder.Services.AddAntiforgery(options =>{
    options.SuppressXFrameOptionsHeader = false;
});

// [1]: creating an IConfiguration object with Configuration builder and passing it as a DI service

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", false, true)
    .Build();
builder.Services.AddSingleton(configuration);

byte[] bConnectionString = Convert.FromBase64String(configuration["HHTCustom"]);
builder.Services.AddDbContext<HHTCustomContext>(options =>
{
    options.UseSqlServer(Encoding.UTF8.GetString(bConnectionString));
    //options.EnableSensitiveDataLogging();
});

builder.Services.AddScoped<ComponentsWrapperManager>();
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

/// Configure the HTTP request pipeline.

// configure swagger behavior for each envirnonment
if (app.Environment.IsProduction() || app.Environment.IsStaging()) {
    app.UseHsts();
    //app.UseHttpsRedirection();
    app.UseSwagger();
    string sVirtualPath = builder.Configuration.GetSection("VirtualPath").Value;
    app.UseSwaggerUI(c => c.SwaggerEndpoint(sVirtualPath + "/swagger/v1/swagger.json","HHT v1"));

}
else {     //development
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseForwardedHeaders(new ForwardedHeadersOptions {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor |
    ForwardedHeaders.XForwardedProto
});

app.UseAuthorization();

app.MapControllers();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

//use custom middleware for logging requests and responses
app.UseMiddleware<APIMiddleware>();

app.Run();



Case 2: Configuration is provided as a default application configuration source by ASP.NET (Works fine with the desired unique ports specified in LaunchProfile.json)

using HHT_API.Databases.HHTCustom;
using HHT_API.Modules.HHT.Models.ResponseModels;
using HHT_API.Modules.Logger.Models;
using HHT_API.Modules.Middleware.API;
using HHT_API.WrapperManagement;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.EntityFrameworkCore;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllersWithViews()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
    })
    .ConfigureApiBehaviorOptions(options =>
    {
        //configure a special response for invalid model state
        options.InvalidModelStateResponseFactory = context =>{
            ModelStateDictionary modelStatePtr = context.ModelState;  
            modelStatePtr.Remove(modelStatePtr.ElementAt(0).Key); //remove the key of the class model itself
            
            List<string> invalidFields = modelStatePtr.Keys.Select(x => x.Replace("$", "").Replace(".", "")).ToList();
            
            var result = new OkObjectResult(new MainResponseModel<object>() {
                MessageCode = (int)StatusCode.ModelValidationError,
                MessageText = $"Invalid entry for ({string.Join(", ", invalidFields)}). Please check your input and try again!",
                Data = null
            });
            return result;
        };
    });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddHsts(options =>
{
    options.MaxAge = TimeSpan.FromSeconds(31536000);
    options.IncludeSubDomains = true;
    options.Preload = true;
});
builder.Services.AddHttpsRedirection(options =>
{
    options.HttpsPort = 443;
    options.RedirectStatusCode = 308;
});

builder.Services.AddAntiforgery(options =>{
    options.SuppressXFrameOptionsHeader = false;
});

// [2] configuration is provided as a default application configuration source by ASP.NET
byte[] bConnectionString = Convert.FromBase64String(builder.Configuration["HHTCustom"]);
builder.Services.AddDbContext<HHTCustomContext>(options =>
{
    options.UseSqlServer(Encoding.UTF8.GetString(bConnectionString));
    //options.EnableSensitiveDataLogging();
});

builder.Services.AddScoped<ComponentsWrapperManager>();
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

/// Configure the HTTP request pipeline.

// configure swagger behavior for each envirnonment
if (app.Environment.IsProduction() || app.Environment.IsStaging()) {
    app.UseHsts();
    //app.UseHttpsRedirection();
    app.UseSwagger();
    string sVirtualPath = builder.Configuration.GetSection("VirtualPath").Value;
    app.UseSwaggerUI(c => c.SwaggerEndpoint(sVirtualPath + "/swagger/v1/swagger.json","HHT v1"));

}
else {     //development
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseForwardedHeaders(new ForwardedHeadersOptions {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor |
    ForwardedHeaders.XForwardedProto
});

app.UseAuthorization();

app.MapControllers();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

//use custom middleware for logging requests and responses
app.UseMiddleware<APIMiddleware>();

app.Run();
0

There are 0 best solutions below