2

I have read through all other posts on this matter and I'm certain this is not a duplicate.

I'm building a Razor page filter to be used in my Startup.cs class and I need access to the HttpContext. I would normally do this through constructor injection using ASP.NET Core's DI capabilities. I even have the usual services.AddHttpContextAccessor() statement in Startup.cs and use it elsewhere in my project.

Since I'm bulding a Razor page filter which derives from IAsyncPageFilter, and is created from the Startup.cs class, it doesn't appear as though I can inject it (since it's created by startup, and not injected).

Here is the addition of the filter to Startup.cs:

Startup.cs (snip)

    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
        options.Filters.Add(new RazorAsyncPageFilter(_logger, Configuration)); <---my filter
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Here is my filter:

RazorAsyncPageFilter.cs

    public class RazorAsyncPageFilter : IAsyncPageFilter
    {
        private readonly ILogger _logger;
        private readonly IConfiguration _configuration;

        public string[] Scopes { get; set; }
        public string ScopeKeySection { get; set; }

        public RazorAsyncPageFilter(ILogger logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }
        public async Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
        {
            await Task.CompletedTask;
        }

        public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
        {
            VaultGraphServiceClient.OnMsalUiExceptionEvent += VaultGraphServiceClientOnOnMsalUiExceptionEvent;
            await next.Invoke();
        }
        
        private void VaultGraphServiceClientOnOnMsalUiExceptionEvent(object sender, MsalUiRequiredException e)
        {
            _logger.LogInformation($"Triggered authentication exception: {e.Message}");
            Scopes = new string[] { _configuration.GetValue<string>(ScopeKeySection) };
            var properties = BuildAuthenticationPropertiesForIncrementalConsent(Scopes, e, **I_NEED_CONTEXT_HERE**);
            new ChallengeResult(properties);
        }
    }

Note the context needed in the method call triggered from an event handler in another class. I'd like to pass the HttpContext to the private method in the filter class.

Here is what I've tried:

  1. Defining a field in Startup.cs for IHttpContextAccessor _httpContext and pass it to the filter however this obviously didn't work as that context isn't established yet.

  2. Pass context from another method. This was a bit more work and is fraught with warnings about passing it to a background thread.

  3. Inject the context from my calling EventHandler. Unfortunately this is being called from a static class and from what I understand, there is no concept of DI in static classes. Is this correct? Additionally, I'm using an EventHandler delegate which I'm using to pass the MsalUiRequiredException object and can only pass one object. Maybe I could use a different handler for the event type but I'm not familiar enough with events to understand how this could be done.

  4. I'm considering creating a custom class to hold both the MsalUiRequiredException and the HttpContext where it can be injected/passed elsewhere but this also seems like overkill or could be a problem as per #2 above.

Any suggestions?

3
  • I have an idea but it is not an answer. So when I did same thing in Asp.Net MVC I created a new class and inherited from WebViewPage abstract class. F.e.: abstract class TViewPage<TModel> : System.Web.Mvc.WebViewPage<TModel> You can used the RazorPage class in Asp.Net Core instead of WebViewPage Commented Nov 30, 2019 at 18:45
  • What is VaultGraphServiceClient.OnMsalUiExceptionEvent Commented Nov 30, 2019 at 18:49
  • @Nkosi it is a subscription to an event triggered from another static class. Commented Nov 30, 2019 at 19:09

1 Answer 1

4

Add IHttpContextAccessor as a constructor parameter

public RazorAsyncPageFilter(ILogger logger, IConfiguration configuration, IHttpContextAccessor httpContext)
{
    _logger = logger;
    _configuration = configuration;
    _httpContext = httpContext;
}

and add the filter by type

options.Filters.Add<RazorAsyncPageFilter>()
Sign up to request clarification or add additional context in comments.

2 Comments

Interesting. So how does adding the filter by type change the DI behavior? Looks like I can just specify ILogger, IConfiguration, and IHttpContextAccessor without passing them. They seem to be available in the RazorAsyncPageFilter class without passing them from Startup.cs. When I added them to the constructor before, the Startup.cs class wanted them all passed in.
@JasonShave-MSFT Probably I didn't understand your question, but it doesn't change DI behavior in any way, it just uses it. While in your initial code you instantiated the filter by yourself and frameworks DI wasn't involved at all

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.