I am adding a global error handler filter in Startup.cs like this:
services.AddMvc(o =>
{
o.Filters.Add(new GlobalExceptionFilter());
});
However, I need to pass in my Email Service which is also being injected. How can I retrieve it from these services in the filter?
public class GlobalExceptionFilter : IExceptionFilter
{
private readonly IEmailService _emailService;
public GlobalExceptionFilter()
{
}
public void OnException(ExceptionContext context)
{
}
}
I use to be able to use DependencyResolver Class to do that in MVC5. Is there a way to accomplish this in core? Or is there a way for me to force instantiation of the service in the Startup so I can pass it as part of the constructor?
I tried looking it up in the services and then looking at ImplementationInstance, but its null at this point so I can't grab it from there it appears. Also keep in mind that my EmailService requires a parameter of IOptions<Settings> so that it can get email settings it needs.
You can use constructor injection.
public class GlobalExceptionFilter : IExceptionFilter
{
private readonly IEmailService emailService;
public GlobalExceptionFilter(IEmailService emailService)
{
this.emailService = emailService;
}
public void OnException(ExceptionContext context)
{
//do something with this.emailService
}
}
But you have to change the way you are registering the global filter in ConfigureServices method. You should use the Add overload which takes a Type
services.AddMvc(o =>
{
o.Filters.Add(typeof(GlobalExceptionFilter));
});
Another option is, explicitly resolving the dependency inside the OnException method by calling the GetService method on HttpContext.RequestServices.
using Microsoft.Extensions.DependencyInjection;
public void OnException(ExceptionContext context)
{
var emailService = context.HttpContext.RequestServices.GetService<IEmailService>();
// use emailService
}
But you should be fine with the first approach. Let the framework resolve it for you and inject to your constructor instead of you trying to do it.
I think best practice for a Global Exception Handler is actually to create a custom middleware step for it. From the documentation
Exception filters are good for trapping exceptions that occur within MVC actions, but they're not as flexible as error handling middleware. Prefer middleware for the general case, and use filters only where you need to do error handling differently based on which MVC action was chosen.
Then you register you classes in the ConfigureServices method:
services.AddTransient<IEmailService, EmailService>();
Then in your Configure method, you register your customer global exception handler. You will want this to be the first thing you do in the Configure method so you catch any exceptions following it in other middleware.
app.UseMiddleware<MyGlobalExceptionHandler>();
And any services you registered will be available for your middleware constructor which might look something like this:
public sealed class MyGlobalExceptionHandler
{
private readonly RequestDelegate _next;
private readonly IEmailService _emailService;
public NlogExceptionHandler(
RequestDelegate next,
IEmailService emailService)
{
_next = next;
_emailService = emailService;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
try
{
_emailService.SendEmail(ex.ToString());
}
catch (Exception ex2)
{
//Its good practice to have a second catch block for simple logging in case the email fails too
}
throw;
}
}
}
Related
I am creating a class which reads the header using IHttpContextAccessor and reads the values from header.
Like ".net framework" we can access context in static manner HttpContext.Current... so shall I inject my class as Singlton or Scoped as the context or context.header value is going to change in each request.
Below is what I am trying
public class RequestHeaderHelper
{
private readonly IHttpContextAccessor httpContext;
public RequestHeaderHelper(IHttpContextAccessor httpContext)
{
this.httpContext = httpContext;
}
public string BrowserIP
{
get
{
if (httpContext.HttpContext.Request.Headers.ContainsKey("X-Client-IP"))
return httpContext.HttpContext.Request.Headers.ContainsKey("X-Client-IP").ToString();
else
return string.Empty;
}
private set { }
}
....
}
Edit 1
Further I am going to use this class inside my other classes like logger. I have created action filter and exception logger where I am going to use this.
I am also using wcf client class to call the service. I am using logging there as well using beforesend and aftersend. I am adding endpoint behavior.
I want to use wcf client as singlton but use the above class in logger.
I have a ASP.NET Core 3.1 WebApi in which I using OAuth based Authentication and Authorization. On the Controllers, I add the Authorize attributes providing the policy name to apply. However, for my local development, I want to skip the authentication/authorization part. Lets say, I add some appsettings to indicate whether to use AA or not. Based on that, in the Configure method in startup class, I can conditionally use below code snippet to activate the required middleware.
if(configuration.GetSection("OAuth:Enabled") == true)
{
app.UseAuthentication();
app.UseAuthorization();
}
However, my controller is still decorated with Authorize attribute. Currently, unless I comment it out on each controller, i cannot disable Authorization. Is there any suggestion on how this can be achieved or any alternate option to temporarily bypass Authorization based on single configuration?
Thanks!
You could just create a custom configuration and wrap Authorize attributes with compiler conditionals e.g.
#if !BYPASS_AUTH
[Authorize()]
#endif
public void Blah(...)
... but this is a bit messy, and if you're like me and don't like seeing compiler directives littered throughout your code, you'd probably like to hide this away in a custom authorize attribute or something where you can do imperative auth as described here.
There's probably multiple ways to do this, but here's one way using an IAsyncActionFilter & TypeFilterAttribute implementations. This approach allows me to dependency inject the IAuthorizationService for imperative auth.
Note: following code is untested, so might be typos and might need refinement...
public class CustomAuthorizeAttribute : TypeFilterAttribute
{
public CustomAuthorizeAttribute(string policyName)
: base(typeof(CustomAuthorizeAsyncActionFilterAttribute))
{
Arguments = new object[] {policyName};
}
}
public class CustomAuthorizeAsyncActionFilterAttribute : Attribute, IAsyncActionFilter
{
private readonly IAuthorizationService _authorizationService;
private readonly AuthSettings _authSettings;
private readonly string _policyName;
public CustomAuthorizeAsyncActionFilterAttribute(
IAuthorizationService authorizationService,
IOptions<AuthSettings> authSettings
string policyName)
{
_authorizationService = authorizationService;
_authSettings = authSettings.Value;
_policyName = policyName;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (_authSettings.AuthEnabled)
{
var authorizationResult = await _authorizationService.AuthorizeAsync(context.HttpContext.User, context.ActionArguments, _policyName);
if (authorizationResult.Succeeded)
{
await next.Invoke();
}
else
{
context.Result = new ForbidResult();
}
}
else
{
await next.Invoke();
}
}
}
Oh and you'll need to change all your [Authorize] attributes to [CustomAuthorize].
I would like to initialize some dependencies resolved from the MassTransit serviceProvider in the same way Asp.Net Core does with the pipeline's middlewares.
In particular I would like to inspect the incoming message before the consumer is called and extract the tenant from it (I'm currently working on a multitenant web application with single database per tenant).
With this informations I need to initialize some scoped instances (Ef Core DbContext for example).
I know that I can inject them in the Consumer through constructor but this means that I must do that everytime I write a new one, so I suppose that a filter should be the right place (correct me if I'm wrong).
The problem raises when I need to access the current consumer scope to resolve the dependencies that I need. I was thinking that the behavior of the MassTransit' pipeline was similar to the Asp.Net one regarding middleware injection but I was probably wrong.
I haven't found any documentation on how to do that clearly without cluttering the code of the filter, so any suggestion is going to be really appreciated.
This is the filter that I need to modify:
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
public void Probe(ProbeContext context) { }
public async Task Send(T context, IPipe<T> next)
{
//Resolve scoped instance here and do something before Consumer is called
var connectionStringProvider = scope.GetService<IConnectionStringProvider>();
await next.Send(context);
}
}
public class RegistrationsDeliveredEventConsumer : IConsumer<IRegistrationsDelivered>
{
private readonly IConnectionStringProvider _connectionStringProvider;
public RegistrationsDeliveredEventConsumer(IConnectionStringProvider connectionStringProvider)
{
//This should be the same instance that has been resolved in the filter' Send() method
_connectionStringProvider = connectionStringProvider;
}
public async Task Consume(ConsumeContext<IRegistrationsDelivered> context)
{
}
}
This is a simplified example of my code but this should be enough
There's two facets to consider: 1) are filters registered as services/pulled from the service collection when using the ASP.NET Core integration and 2) what lifetime do the filters have if they are. I'm not familiar with the MassTransit ASP.NET Core integration, but it looks like you should be good based on a cursory review. You'll need to confirm that both of those requirements are met.
For dependency injection, in general, constructor injection is the way to go unless there's a very specific need to do something different, which does not seem to be the case here. In short, you need a constructor for your filter.
What exactly you need to inject is a function of the lifetime of the filter. If it has a transient lifetime, then you can inject your scoped dependencies directly. If it has a singleton lifetime, then you'll need to inject IServiceProvider instead, and do the following whenever you need to use one of those dependencies:
using (var scope = _serviceProvider.CreateScope())
{
var dep = scope.ServiceProvider.GetRequiredService<MyDependency>();
// do something with `dep`
}
Here's a draft... I'm sure there are missing pieces, so let me know if you have questions.
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
private readonly Func<string, IDbConnection> _dbContextAccessor;
public void Probe(ProbeContext context) { }
public TenantContextInitializerFilter(Func<string, IDbConnection> dbContextAccessor)
{
_dbContextAccessor = dbContextAccessor;
}
public async Task Send(T context, IPipe<T> next)
{
var tenantId = ""; // place holder
using (var dbContext = _dbContextAccessor(tenantId))
{
//... do db logic
}
await next.Send(context);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IConnectionStringProvider>(
provider => null /* TODO figure out how to fetch scoped instance from a cache or some storage mechanism*/);
services.AddScoped(provider =>
{
IDbConnection Accessor(string tenantId)
{
if (provider.GetService<IConnectionStringProvider>()
.TryGetConnectionString(tenantId, out var connectionString, out var providerName))
return new SqlConnection(connectionString);
throw new Exception();
}
return (Func<string, IDbConnection>)Accessor;
});
}
}
We are starting with ASP.NET Core 2. We need a way for each element that is involved in a request to write a message to a message handler.
Some limitations:
We won't use HttpContext.Items (HttpContext is not available in the class that we are using inside the Controller, and we don't like to forward the whole context there).
We tried to use it without dependency injection because if we have multiple different services, we will have too many parameters in the constructors.
Must also work with async/await.
We tried an approach using AsyncLocal<T>.
For that we created a class:
public class NotificationExecutionContext
{
private static readonly AsyncLocal<NotificationHandler> NotificationHandler =
new AsyncLocal<NotificationHandler>();
public static NotificationHandler Instance =>
NotificationHandler.Value ?? (NotificationHandler.Value = new NotificationHandler());
}
There will be a NotificationHandler created, which should live per-request. The NotificationHandler is a simple class where you can add/get messages to/from a collection:
public class NotificationHandler : INotificationHandler
{
public List<NotificationBase> Notifications { get; } = new List<NotificationBase>();
public void AddNotification(NotificationBase notification)
{
Notifications.Add(notification);
}
public void AddNotificationRange(List<NotificationBase> notifications)
{
Notifications.AddRange(notifications);
}
}
With this solution, I can easily get the NotificationHandler for this context and add a notification.
NotificationExecutionContext.Instance.AddNotification(new NotificationBase(){..})
Inside a middleware, we are waiting on the Response.OnStarting() event and then we take all messages from the NotificationHandler and add them the response header:
public async Task Invoke(HttpContext context)
{
var e = NotificationExecutionContext.Instance; // Required so that notification handler will be created in this context
context.Response.OnStarting((state) =>
{
List<NotificationBase> notifications = NotificationExecutionContext.Instance.Notifications;
if (notifications.Count > 0)
{
string messageString = JsonConvert.SerializeObject(notifications, Formatting.None);
context.Response.Headers.Add("NotificationHeader", messageString);
}
return Task.FromResult(0);
}, null);
await Next(context);
}
This code works, but are there pitfalls that we do not know? Or are there better solutions?
You should not use static singletons like that. Having static dependencies like that inside your code defeats the whole purpose of dependency injection. You should just embrace dependency injection here, which would make this super simple:
/* in Startup.ConfigureServices */
// register the notification handler as a scoped dependency, this automatically makes the
// instance shared per request but not outside of it
services.AddScoped<INotificationHandler, NotificationHandler>();
/* in Startup.Configure */
// register your custom middleware
app.Use<NotificationHandlerMiddleware>();
public class NotificationHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly NotificationHandler _notificationHandler;
public NotificationHandlerMiddleware(RequestDelegate next, INotificationHandler notificationHandler)
{
_next = next;
_notificationHandler = notificationHandler;
}
public void Invoke(HttpContext context)
{
// do whatever with _notificationHandler
await _next(context);
}
}
And that’s all. No need to introduce statics, but using full dependency injection making your code completely testable and all dependencies clear.
We tried to use it without dependency injection because if we have multiple different services we will have to many parameters in the constructors.
Too many constructor parameters is a clear sign for a violation of the single responsibility principle. If you find your services take many dependencies, you should consider splitting it up. You may also want to consider refactoring to facade services.
I'm trying to refactor some code to use .NET Core dependency injection via mapping services in startup.cs. I would like to inject an IRequestDatabaseLogger here instead of newing it up. However it requires the context in the constructor. How can I achieve this? Is it even possible without an DI framework or even then?
public class ActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var requestDatabaseLogger = new RequestDatabaseLogger(context);
long logId = requestDatabaseLogger.Log();
context.HttpContext.AddCurrentLogId(logId);
base.OnActionExecuting(context);
}
}
However it requires the context in the constructor.
Letting the construction of application components depend on runtime data is an anti-pattern, as described here. That article describes how to solve these problems in general.
In your case this probably means that your component should depend on ASP.NET Core's IHttpContextAccessor abstraction instead, which is a pattern described in the referenced article.
Alternatively, as described in the article, you can pass through the required runtime data to the logger using it's Log method.
You should use the TypeFilter to achieve this, and wrap the filter that has the dependency (in this case on a logger or a context) inside of the filter. I show a detailed example of this in my MSDN Article on ASP.NET Core Filters. The related source code is here (look at the ValidateAuthorExists filter).
Here's what it might look like in your scenario:
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute():base(typeof(MyFilterImpl))
{
}
private class MyFilterImpl : IAsyncActionFilter
{
public MyFilterImpl( *inject dependencies here*)
{}
}
}
This is how you can use attributes in .NET Core while still injecting dependencies into the underlying action filter. I also cover this in my upcoming ASP.NET Core Quickstart course on DevIQ.com (look for it end of this month).
Inject a RequestDatabaseLoggerFactory in the constructor, which can be used to create a RequestDatabaseLogger instance.
public interface IRequestDatabaseLoggerFactory {
IRequestDatabaseLogger Create(ActionExecutingContext context);
}
public class RequestDatabaseLoggerFactory : IRequestDatabaseLoggerFactory {
public IRequestDatabaseLogger Create(ActionExecutingContext context) {
return new RequestDatabaseLogger(context);
}
}
public class ActionFilter : ActionFilterAttribute
{
public ActionFilter(IRequestDatabaseLoggerFactory factory) {
_factory = factory;
}
private readonly IRequestDatabaseLoggerFactory _factory;
public override void OnActionExecuting(ActionExecutingContext context)
{
var requestDatabaseLogger = _factory.Create(context);
long logId = requestDatabaseLogger.Log();
context.HttpContext.AddCurrentLogId(logId);
base.OnActionExecuting(context);
}
}