I am experiencing some issues with the AspNetCore.HealthChecks.UI package. I made the necessary settings in the Startup class of my API project, but when I access the endpoint to view the HealthCheck interface, only a JSON is displayed.
My HealthCheck UI
My project is on version 3.1 of .NET Core, in which I installed the following nuget packages:
AspNetCore.HealthChecks.UI v3.1.3
AspNetCore.HealthChecks.UI.Client v3.1.2
AspNetCore.HealthChecks.UI.InMemory.Storage v3.1.2
Microsoft.Extensions.Diagnostics.HealthChecks v5.0.9
Below is the extension method in which I use the HealthCheck settings:
public static IServiceCollection AddHealthCheckConfiguration(
this IServiceCollection services,
IConfiguration configuration)
{
string mongoConnectionString = configuration.GetSection("MongoSettings").GetSection("Connection").Value;
string mongoDatabaseName = configuration.GetSection("MongoSettings").GetSection("DatabaseName").Value;
string redisConnectionString = configuration.GetConnectionString("Redis");
services
.AddHealthChecks()
.AddRedis(redisConnectionString)
.AddMongoDb(mongoConnectionString, mongoDatabaseName: mongoDatabaseName);
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(15);
opt.MaximumHistoryEntriesPerEndpoint(60);
opt.SetApiMaxActiveRequests(1);
opt.AddHealthCheckEndpoint("default api", "/api-health");
})
.AddInMemoryStorage();
return services;
}
And in another extension method I add the necessary settings for displaying the interface:
public static IApplicationBuilder UseMvcConfiguration(this IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/api-health", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecksUI();
});
return app;
}
Finally, my Startup class looks like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddApiResponseCompressionConfig();
services.AddApiCachingConfig(Configuration);
services.AddHateoasConfig();
services.AddMongoDbConfig();
services.AddAutoMapper(typeof(Startup));
services.AddWebApiConfig();
services.AddHealthCheckConfiguration(Configuration);
services.ResolveGeneralDependencies();
services.ResolveRepositories();
services.ResolveApplicationServices();
services.AddSwaggerConfig();
}
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IApiVersionDescriptionProvider provider)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHealthChecks("/healthcheck");
app.UseMvcConfiguration();
app.UseSwaggerConfig(provider);
}
}
I searched for similar errors here on StackOverflow, but none of the answers I found solved my problem.
Thanks to #fbede help, I managed to solve the problem with the question.
I wasn't correctly mapping the healthcheck generation endpoints and the monitoring interface itself.
It was necessary to make adjustments to the code I presented in the question, starting with my extension method:
public static IServiceCollection AddHealthCheckConfiguration(
this IServiceCollection services,
IConfiguration configuration)
{
string mongoConnectionString = configuration.GetSection("MongoSettings").GetSection("Connection").Value;
string mongoDatabaseName = configuration.GetSection("MongoSettings").GetSection("DatabaseName").Value;
services
.AddHealthChecks()
.AddMongoDb(mongoConnectionString, mongoDatabaseName: mongoDatabaseName);
services.AddHealthChecksUI().AddInMemoryStorage();
return services;
}
And ending with the Configure method of my Startup class:
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IApiVersionDescriptionProvider provider)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHealthChecks("/healthcheck", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecksUI(options =>
{
options.UIPath = "/healthchecks-ui";
options.ApiPath = "/health-ui-api";
});
app.UseMiddleware<ExceptionMiddleware>();
app.UseMvcConfiguration();
app.UseSwaggerConfig(provider);
}
}
Also, I removed the following unnecessary configuration:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/api-health", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecksUI();
});
These fixes led to the expected loading of the interface:
Related
I have two applications running together at the same time. I was trying to find a way to be able to use TempData in my own type of class and after reading it I implemented it in my Middleware for my MVC project which works smoothly. However, when I copy the Middleware code from my MVC project to my Middleware for my asp.net web api project it does not work. When I run the programs together, and when it calls the web api project it returns the following the web api (MVC works fine I do not get any errors on that):
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory' while attempting to activate 'AddressService.API.Middleware.CorrelationIdMiddleware'.
Before I implemented TempData (ITempDataDictionaryFactory) in the middleware of my web api project it worked fine... but after implementing ITempDataDictionaryFactory to it, it gives me that error. Is there something I have to do in order for it to work like it does in my Middleware for my MVC project?
Middleware in my web api project:
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;
public CorrelationIdMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ITempDataDictionaryFactory tempDataDictionaryFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<CorrelationIdMiddleware>();
_tempDataDictionaryFactory = tempDataDictionaryFactory;
}
public async Task Invoke(HttpContext context)
{
string correlationId = null;
string userName;
string ipAddress;
var tempData = _tempDataDictionaryFactory.GetTempData(context);
var key = context.Request.Headers.Keys.FirstOrDefault(n => n.ToLower().Equals("x-correlation-id"));
if (!string.IsNullOrWhiteSpace(key))
{
correlationId = context.Request.Headers[key];
_logger.LogInformation("Header contained CorrelationId: {#CorrelationId}", correlationId);
}
else
{
if (tempData.ContainsKey("username") && tempData.ContainsKey("ipaddress"))
{
userName = tempData.Peek("username").ToString();
ipAddress = tempData.Peek("ipaddress").ToString();
context.Response.Headers.Append("X-username", userName);
context.Response.Headers.Append("X-ipAddress", ipAddress);
}
correlationId = Guid.NewGuid().ToString();
_logger.LogInformation("Generated new CorrelationId: {#CorrelationId}", correlationId);
}
context.Response.Headers.Append("x-correlation-id", correlationId);
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await _next.Invoke(context);
}
}
CorrelationIdExtensions.cs (use to call app.UseCorrelationId() in startup):
public static class CorrelationIdExtensions
{
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CorrelationIdMiddleware>();
}
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "AddressService.API", Version = "v1" });
});
services.AddHttpContextAccessor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCorrelationId();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AddressService.API v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
One of the ways to solve this should be using:
services.AddControllersWithViews();
or
services.AddMvc();
Instead of services.AddControllers();.
I have a default web api template project using .NET Core 3.1 and I have registered Elastic Search NEST on my startup.cs. But when I load it, it hit error at
Singleton ImplementationType: Unable to resolve service for type 'Nest.IElasticClient' while attempting to activate in program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
and here is my startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IProductService, ESProductService>();
services.Configure<ProductSettings>(Configuration.GetSection("product"));
services.AddElasticsearch(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
and below is the ElasticsearchExtensions extension class
public static class ElasticsearchExtensions
{
public static void AddElasticsearch(this IServiceCollection services, IConfiguration configuration)
{
var url = configuration["elasticsearch:url"];
var defaultIndex = configuration["elasticsearch:index"];
var settings = new ConnectionSettings(new Uri(url))
.DefaultIndex(defaultIndex);
AddDefaultMappings(settings);
var client = new ElasticClient(settings);
services.AddSingleton(client);
CreateIndex(client, defaultIndex);
}
private static void AddDefaultMappings(ConnectionSettings settings)
{
settings
.DefaultMappingFor<Product>(m => m
.Ignore(p => p.Price)
.Ignore(p => p.Quantity)
.Ignore(p => p.Rating)
);
}
private static void CreateIndex(IElasticClient client, string indexName)
{
var createIndexResponse = client.Indices.Create(indexName,
index => index.Map<Product>(x => x.AutoMap())
);
}
}
Problem solved. The AddSingleton miss out the interface. services.AddSingleton<IElasticClient>(client);
I also got the same error. To fix the issue, I used IElasticClientService instead of IElasticClient. You can use below code for dependency Injection.
builder.Services.AddScoped<IElasticClientService, ElasticClientService>();
ElasticClientService has a property called "elasticClient" which can be used to do all sort of operations. Hope this helps.
I have ASP.NET Core application where I need to get database credentials from external AWS service. Essentially, I need to inject CredentialRetrievalService into Startup.cs. Awhile ago, I found an example that describes perfectly how to do it:
In Program.cs:
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(serviceCollection =>
serviceCollection.AddScoped<ISomeService, SomeService>())
.UseStartup<Startup>()
.Build();
}
and in Startup.cs:
private ISomeService _someService;
public Startup(IConfiguration configuration, ISomeService someService)
{
_someService = someService;
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Just to see that everything works - and it does!
services.AddMvc(options =>
{
options.MaxModelValidationErrors = _someService.GetNumber(Configuration.GetValue<int>("MaxModelValidationErrors"));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
Now I am trying to upgrade the application to ASP.NET Core 3. I can see that there is breaking change, and the code above doesn't work any more. Recommended action is to Inject services into the Startup.Configure method instead. But what should I do if I need to invoke injected service in ConfigureServices?
After more analysis, this is what seems to work:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddScoped<ISomeService, SomeService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<MvcOptions> options, ISomeService someService)
{
// ...
int value = Configuration.GetValue<int>("MaxModelValidationErrors");
options.Value.MaxModelValidationErrors = someService.GetNumber(value);
// ...
}
But it doesn't work with my real application where SomeService is DbContext
Consider changing the approach and taking advantage of dependency injection when configuring options
Reference Use DI services to configure options
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
//...
services.AddScoped<ISomeService, SomeService>();
services.AddMvc(); //options will be configured lower down
//Use DI services to configure MVC options
services.AddOptions<MvcOptions>()
.Configure<ISomeService>((options, someService) => {
int value = Configuration.GetValue<int>("MaxModelValidationErrors");
options.MaxModelValidationErrors = someService.GetNumber(value);
});
//...
}
//...
i wqant ot use the autofac in my project .
i write this startup :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly())
.AsImplementedInterfaces();
builder.Populate(services);
builder.AddDispatchers();
var conteiner = builder.Build();
return new AutofacServiceProvider(conteiner);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
nad this is my program.cs :
public class Program
{
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}
but it show me this errro :
'ConfigureServices returning an System.IServiceProvider isn't supported.'
How can is solve this problem?
This is because you are trying the pre 3.0 way. Check the ConfigureServices docs. It does not supprot the IServiceProvider return type.
public virtual void ConfigureServices (Microsoft.Extensions.DependencyInjection.IServiceCollection services);
From the autofac docs:
This is not for ASP.NET Core 3+ or the .NET Core 3+ generic hosting support - ASP.NET Core 3 has deprecated the ability to return a service provider from ConfigureServices
Check Autofac net core guide post 3.0
public class Program
{
public static void Main(string[] args)
{
// ASP.NET Core 3.0+:
// The UseServiceProviderFactory call attaches the
// Autofac provider to the generic hosting mechanism.
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder => {
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}
public class Startup
{
public Startup(IHostingEnvironment env)
{
// In ASP.NET Core 3.0 `env` will be an IWebHostingEnvironment, not IHostingEnvironment.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; private set; }
public ILifetimeScope AutofacContainer { get; private set; }
// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the ConfigureContainer method, below.
public void ConfigureServices(IServiceCollection services)
{
// Add services to the collection. Don't build or return
// any IServiceProvider or the ConfigureContainer method
// won't get called.
services.AddOptions();
}
// ConfigureContainer is where you can register things directly
// with Autofac. This runs after ConfigureServices so the things
// here will override registrations made in ConfigureServices.
// Don't build the container; that gets done for you by the factory.
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac, like:
builder.RegisterModule(new MyApplicationModule());
}
// Configure is where you add middleware. This is called after
// ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
// here if you need to resolve things from the container.
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory)
{
// If, for some reason, you need a reference to the built container, you
// can use the convenience extension method GetAutofacRoot.
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
}
}
I am getting an error when trying to call the controller below using Lamar to resolve the dependencies at runtime.
I have tried .AddControllersAsServices() and without and still get the same result.
Using
ASP.NET Core: 3.1
Lamar
Container.GetInstance<IDataAccess>() works inside the watch window but will not resolve at runtime
Container.WhatDoIHave() also shows that the dependency is there
Question?
What am I missing in Lamar configuration to resolve the controllers?
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly IDataAccess _dataAccess;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(IDataAccess dataAccess, ILogger<WeatherForecastController> logger)
{
_dataAccess = dataAccess;
}
[HttpGet]
public IEnumerable<string> Get()
{
return _dataAccess.GetAll();
}
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IContainer Container { get; private set; }
public void ConfigureContainer(ServiceRegistry services)
{
Container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssembliesAndExecutablesFromApplicationBaseDirectory(a =>
a.FullName.Contains("Test3.1"));
scanner.WithDefaultConventions();
scanner.SingleImplementationsOfInterface();
});
});
services
.AddControllers(options =>
{
// Disable automatic fallback to JSON
options.ReturnHttpNotAcceptable = true;
// Honor browser's Accept header (e.g. Chrome)
options.RespectBrowserAcceptHeader = true;
})
.AddControllersAsServices();
services.AddMvc()
.AddControllersAsServices();
Container.WhatDidIScan();
Container.WhatDoIHave();
Console.Write("Container Instantiated");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseLamar()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
});
}
An unhandled exception occurred while processing the request.
LamarException: Cannot build registered instance weatherForecastController of 'Test3._1.Controllers.WeatherForecastController':
Cannot fill the dependencies of any of the public constructors
Available constructors:new WeatherForecastController(IDataAccess dataAccess, ILogger<Test3._1.Controllers.WeatherForecastController> logger)
* IDataAccess is not registered within this container and cannot be auto discovered by any missing family policy
The error message indicates that the container can't resolve the controller's dependencies. Make sure those dependencies are registered with the container so it knows how to resolve them when activating controllers.
This is because separate containers are being configured in Startup and the one used by the framework is unaware of IDataAccess as the Scan was not applied to its container.
Reference Lamar - Integration with ASP.Net Core
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
//REMOVED IContainer. It is not needed
public void ConfigureContainer(ServiceRegistry services) {
//Apply scan to the registry used by framework so container is aware of types.
services.Scan(scanner => {
scanner.AssembliesAndExecutablesFromApplicationBaseDirectory(a =>
a.FullName.Contains("Test3.1"));
scanner.WithDefaultConventions();
scanner.SingleImplementationsOfInterface();
});
services
.AddControllers(options => {
// Disable automatic fallback to JSON
options.ReturnHttpNotAcceptable = true;
// Honor browser's Accept header (e.g. Chrome)
options.RespectBrowserAcceptHeader = true;
})
.AddControllersAsServices();
services.AddMvc()
.AddControllersAsServices();
services.WhatDidIScan();
services.WhatDoIHave();
Console.Write("Container Instantiated");
}
//...omitted for brevity
}