user924069
user924069

Reputation:

How to override HttpGet/HttpPost in Asp.net MVC 5 ApiController

I would like to override the API Controller to check for certain values in the header on all HttpGet and HttpPost calls as they're made without including the code for the check in every single call. Currently my method looks like:

public class MyApiController : ApiController
{
    [HttpGet]
    public HttpResponseMessage GetAccountById()
    {
        var accountId = (Request.Headers.Where(t => t.Key == "accountid").Count() == 0) ? null : Request.Headers.GetValues("accountid").First();
        var apiKey = (Request.Headers.Where(t => t.Key == "apikey").Count() == 0) ? null : Request.Headers.GetValues("apikey").First();

        if (String.IsNullOrEmpty(accountId)) {
                return Request.CreateResponse(HttpStatusCode.Forbidden, "Please provide an Account Id.");
        }

        if (String.IsNullOrEmpty(apiKey)) {
                return Request.CreateResponse(HttpStatusCode.Forbidden, "Please provide an Account Api Key.");
        }

        // Get Account
        // return Account;
    }
}

How can I do that apikey/accountid check in every call without having to write the check into every call?

SOLUTION: Overriding the DelegatingHandler worked perfectly.

ApiSecurityHandler.cs

public class ApiSecurityHandler : DelegatingHandler
{
    public ApiSecurityHandler(HttpConfiguration httpConfiguration)
    {
        InnerHandler = new HttpControllerDispatcher(httpConfiguration); 
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var accountId = (request.Headers.Where(t => t.Key == "accountid").Count() == 0) ? null : request.Headers.GetValues("accountid").First();
        var apiKey = (request.Headers.Where(t => t.Key == "apikey").Count() == 0) ? null : request.Headers.GetValues("apikey").First();

        if (String.IsNullOrEmpty(accountId)) {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            response.Content = new StringContent("Please provide an Account Id.");

            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }

        if (String.IsNullOrEmpty(apiKey)) {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            response.Content = new StringContent("Please provide an Account Api Key.");

            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }

        // Authorize the Account Id and Api Key here
        using (var accountManager = new AccountManager()) {
            if (!accountManager.AuthorizeAccountApiKey(accountId, apiKey)) {
                var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                response.Content = new StringContent("Api authorization denied.");

                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(response);
                return tsc.Task;
            }
        }

        return base.SendAsync(request, cancellationToken);
    }
}

And the in your routing config just add this parameter to the map route:

handler: new ApiSecurityHandler(GlobalConfiguration.Configuration)

Upvotes: 1

Views: 3962

Answers (3)

DmitryK
DmitryK

Reputation: 1333

I would recommend the use of DelegatingHandler.

This example from msdn show how to override your Header

This code should work, enjoy:

 public class Myhandler: DelegatingHandler
 {
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessagerequest,   CancellationToken cancellationToken)
    { 
        if(request.Headers.Contains("accountid") && request.Headers.Contains("apikey")) 
        {

            string accountid = request.Headers.GetValues("accountid").FirstOrDefault();
            string apikey = request.Headers.GetValues("apikey").FirstOrDefault();

                //HERE you can get your account and do what you want
        }else{
              return SendError("please provide account id and api key", HttpStatusCode.Forbidden);
        }

           return base.SendAsync(request, cancellationToken);
    }




    private Task<HttpResponseMessage> SendError(string error, HttpStatusCode code)
    {
        var response = new HttpResponseMessage();
        response.Content = new StringContent(error);
        response.StatusCode = code;

        return Task<HttpResponseMessage>.Factory.StartNew(() => response);
    }

}

one more example of DelegatingHandler

More examples

Upvotes: 2

user1477388
user1477388

Reputation: 21440

What I would do, instead of overriding ApiController is, create a base class that inherits ApiController, and do your coding there. Like this:

public class APIBaseController : ApiController
{
    [HttpGet]
    public void APIBaseController() {
        //Request.Headers.Count()
    }

    [HttpPost]
    public void APIBaseController() {
        //Request.Headers.Count()
    }
}

And then do this:

public class MyApiController : APIBaseController 

Upvotes: 0

spender
spender

Reputation: 120548

Write yourself a subclass of System.Web.Http.Filters.AuthorizationFilterAttribute, with your own implementation of OnAuthorization then you can use this attribute on individual controller methods or on the controller itself.

Here's an example of someone doing exactly this.

Upvotes: 1

Related Questions