govin
govin

Reputation: 6693

Writing a filter that applies to both MVC and Web API requests

My ASP.NET application uses both ASP.NET MVC for serving web pages and ASP.NET Web API for serving AJAX requests that originate from those web pages.

I would like certain things such as checking if the request is authenticated, setting appropriate things in the HttpContext to happen regardless of which kind of request I'm dealing with.

I'm currently having to write two classes

1) one that inherits - System.Web.Mvc.ActionFilterAttribute for MVC requests 2) one that inherits - System.Web.Http.Filters.ActionFilterAttribute for Web API requests

Is there a way to apply a filter that runs for both MVC and API requests to the website? or is going via the old http modules the recommended way for such a use case?

Upvotes: 5

Views: 1644

Answers (2)

Ken Kin
Ken Kin

Reputation: 4693

This can be achieved by using proxy pattern. Here's a simplified EnableCorsAttribute for an example to show how:

using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Mvc;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System;

public class EnableCorsAttribute:System.Web.Mvc.ActionFilterAttribute, System.Web.Http.Filters.IActionFilter {
    class HttpActionFilter:System.Web.Http.Filters.ActionFilterAttribute {
        public override void OnActionExecuted(HttpActionExecutedContext filterContext) {
            var res = filterContext.Response;
            res.Headers.Add("Access-Control-Allow-Origin", String.Join(",\x20", m_httpFilter.Origins));
            res.Headers.Add("Access-Control-Allow-Headers", String.Join(",\x20", m_httpFilter.Headers));
            res.Headers.Add("Access-Control-Allow-Credentials", "true");
            base.OnActionExecuted(filterContext);
        }

        public HttpActionFilter(EnableCorsAttribute filter) {
            m_httpFilter=filter;
        }

        EnableCorsAttribute m_httpFilter;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        var res = filterContext.HttpContext.Response;
        res.Headers.Add("Access-Control-Allow-Origin", String.Join(",\x20", this.Origins));
        res.Headers.Add("Access-Control-Allow-Headers", String.Join(",\x20", this.Headers));
        res.Headers.Add("Access-Control-Allow-Credentials", "true");
        base.OnActionExecuted(filterContext);
    }

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
        return m_mvcFilter.ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
    }

    public EnableCorsAttribute(String origins, String headers, String methods) {
        m_mvcFilter=new HttpActionFilter(this);

        var separator = new[] { ',' };
        var options = StringSplitOptions.RemoveEmptyEntries;
        this.Origins=origins.Split(separator, options);
        this.Headers=headers.Split(separator, options);
        this.Methods=methods.Split(separator, options);
    }

    public IList<String> Origins {
        get; private set;
    }

    public IList<String> Headers {
        get; private set;
    }

    public IList<String> Methods {
        get; private set;
    }

    System.Web.Http.Filters.IActionFilter m_mvcFilter;
}

Upvotes: 0

Kevin Junghans
Kevin Junghans

Reputation: 17540

I do not see a good way to put filters for authentication/authorization in MVC and Web API into a single filter because they have very different behaviors. For an MVC request when the user fails authorization you want to redirect them to another page to either logon as another user or just let them know they do not have access to that page. For a Web API request when the user fails authorization you want to send an HTTP status code that indicates authorization failed and let the client handle it. They can even digress more in behavior and functionality if for example you add basic authentication to your filter as described in this article. This only makes sense for Web API request, not MVC requests. I think it is a cleaner separation to have two different filters.

Upvotes: 0

Related Questions