Damien Brils
Damien Brils

Reputation: 75

Catch an invalid HTTP request method globally

I would like to restrict my Web API endpoints to certain HTTP methods, such as GET and POST. I have searched the internet and I found out that you can add either [HttpGet] or [HttpPost] above your method as follows:

[HttpPost]
public ActionResult Login(string userName, string password) {
    // do login stuff
    return View();
}

Now I want to test if the example above with [HttpPost] really works so I use postman to send a HTTP request to my Web API. I fill in the URI and set the method to GET. The response I get is as follows:

{
    "message": "The requested resource does not support http method 'POST'."
}

I'm able to verify that adding [HttpPost] prevents me from using HTTP GET requests.

What I would like to do now is log the event whenever an user tries to sent GET requests when the application is expecting POST, and vice versa. I could implement something for every single method but this would take a lot of time and it wouldn't be easy to make changes once it's been implemented. So I would like to filter it globally or something.

For example something like:

class MethodRequestFilter : SomeGlobalMethodFilter
    {
        public override void Filter(SomeRequestContext requestContext)
        {
            if (usedRequestMethod.isNotValid == true)
            {
                //implementation for logging
            }
        }
    }

But ofcourse I haven't been able to find this yet within the libraries of .Net. How can I log the event globally whenever a user tries to make a request that isn't a supported method?

Greetings,

Damien.

Upvotes: 1

Views: 1238

Answers (2)

Damien Brils
Damien Brils

Reputation: 75

Thanks to the user div I was able to solve my problem by using a base controller that implements logging. These are the steps that I had to take:

  1. Create a new controller class called BaseController and inherit ApiController:

  2. Override the ExecuteAsync method from ApiController:

  3. Add an implementation for logging in the catch clause

  4. Inherit the new BaseController in every controller class that you would like to have logging functionality.

The code that I used in my implementation:

public class BaseController : ApiController
{
    public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
    {
        try
        {
            HttpResponseMessage response = await base.ExecuteAsync(controllerContext, cancellationToken);

            return response;
        }
        catch (HttpResponseException ex)
        {               
            if (ex.Response.StatusCode == HttpStatusCode.MethodNotAllowed)
            {
                //Logging implementation
            }
            return Request.CreateErrorResponse(ex.Response.StatusCode, ex.Message);
        }   
    }            
}

If there is any way to make my code better, please let me know :)

Upvotes: 1

Divyang Desai
Divyang Desai

Reputation: 7864

One way is to using common base controller, to implement you need to add one base controller which would inherited from ApiController

public class BaseController : ApiController
{
    public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
    {            
        try
        {
            HttpResponseMessage response = await base.ExecuteAsync(controllerContext, cancellationToken);
            if (!response.IsSuccessStatusCode) // or if(response.StatusCode == HttpStatusCode.BadRequest) 
            {
                //log here 
            }
            return response;
        }
        catch(Exception ex)
        {
            return await InternalServerError(ex).ExecuteAsync(cancellationToken);
        }            
    }
}  

Now, let's assume that you're having ValuesController and Login method and it supports only POST, here your all other controllers inherit from BaseController instead ApiController

public class ValuesController : BaseController
{ 
  [HttpPost]
  public void Login([FromBody]string value)
  {
  }
}   

So, once you call your login method, it'll call BaseController method first and you will get response there.

Hope this helps!

Upvotes: 1

Related Questions