ASP.NET Web API message handler - c#

I want to implement my custom message handler that will check for a custom header that must be present in every request.
If my custom header is present the request passes through and will hit the controller if the header is not there the request is rejected with custom error message.
No my question is: if I implement my handler that way that means all requests MUST have the header however I need to have a gate where I can call without that header and the request must be ignored by message handler and hit the controller even without the custom header.
Is it possible to achieve this? Or how can I implement my message handler that will ignore certain calls to specific controller or something like this ...?

You can try this.. (untested)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
public abstract class EnforceMyBusinessRulesController : ApiController
{
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
/*
Use any of these to enforce your rules;
http://msdn.microsoft.com/en-us/library/system.web.http.apicontroller%28v=vs.108%29.aspx
Public property Configuration Gets or sets the HttpConfiguration of the current ApiController.
Public property ControllerContext Gets the HttpControllerContext of the current ApiController.
Public property ModelState Gets the model state after the model binding process.
Public property Request Gets or sets the HttpRequestMessage of the current ApiController.
Public property Url Returns an instance of a UrlHelper, which is used to generate URLs to other APIs.
Public property User Returns the current principal associated with this request.
*/
base.Initialize(controllerContext);
bool iDontLikeYou = true; /* Your rules here */
if (iDontLikeYou)
{
throw new HttpResponseException(new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
}
}
}
public class ProductsController : EnforceMyBusinessRulesController
{
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
}
}

Related

Add header to response if requested version is deprecated

I would like to add a custom header to my service's response if a deprecated version is requested.
I already have URL-based versioning set up using Microsoft.AspNetCore.Mvc.Versioning, and I additionally have an existing custom ActionFilter class that can write custom headers into the response. I can also get the version requested by the client using context.HttpContext.GetRequestedApiVersion() inside my ActionFilter's definition for OnActionExecuted(ActionExecutedContext context).
However, I'm not sure how I can check whether the requested version is a deprecated version or not from my custom ActionFilter. The documentation on deprecating a service version does not answer this question and I can't find the answer among any of the rest of the documentation on github.
My controller class is annotated as follows:
[ApiVersion("2", Deprecated=true)]
[Route("api/v{v:apiVersion}/[action]")]
[ApiController]
public class CustomControllerV2 : ControllerBase { ... }
[ApiVersion("3")]
[Route("api/v{v:apiVersion}/[action]")]
[ApiController]
public class CustomControllerV3 : ControllerBase { ... }
And this is my custom ActionFilter:
public class CustomActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) {
// not implemented
}
public void OnActionExecuted(ActionExecutedContext context) {
var requestedApiVersion = context.HttpContext.GetRequestedApiVersion();
if (
// Check whether version is deprecated here
) {
context.HttpContext.Response.Headers.Add("warning", "Requested version "+requestedApiVersion.ToString()+" is deprecated.");
}
}
}
From the incoming request you will see what the URL is, and therefore you can determine which controller it will be mapped to. Controllers are classes, and the deprecated ones are marked with the [ApiVersion(Deprecated = true)] attribute. So you can grab the controller, and with reflection you can check its attributes and see if it's deprecated. Documentation here

Is ASP.NET Core MVC filter singleton?

I have an AuthorizationFilter as follows:
public class AuthorizationFilterAttribute : Attribute, IAuthorizationFilter
{
public AuthorizationFilterAttribute()
{
//Constructor of AuthorizationFilter will be called one time
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//OnAuthorization method will be called per http request
}
}
I found the constructor of AuthorizationFilter will just be called one time during the whole ASP.NET Core application lifetime. But its OnAuthorization method will be called per HTTP request.
Does it mean all filters (including IAuthorizationFilter,IActionFilter,IResourceFilter,IExceptionFilter etc) in ASP.NET Core MVC are singletons, which means they will be created just one time during ASP.NET Core application lifetime?
It depends on the IFilterFactory.IsReusable property that is associated with your filter.
When the IFilterProvider (which by default is DefaultFilterProvider) is about to provide the desired instance, it first checks whether the filter implements IFilterFactory as well:
If it does, it uses the filter's own IsReusable property to determine the lifetime of the instance.
If not, it assumes the filter is reusable and IsReusable is set to true.
In the case of your custom AuthorizationFilterAttribute, since you don't implement IFilterProvider, it's indeed considered as reusable and will be created only once.
See Source
Thanks for haim770's answer.
I found an implementation that can create a filter instance per http request rather than ASP.NET Core application lifetime.
Actually, we need to create the filter internally and wrap it into an IFilterFactory like below:
using Microsoft.AspNetCore.Mvc.Filters;
using System;
namespace AspNetCoreFilterDemo.Filters
{
public class AuthorizationFilterWithFactoryAttribute : Attribute, IFilterFactory
{
//Return false, IFilterFactory.CreateInstance method will be called per http request
//Return true, InternalAuthorizationFilter will still be singleton, since IFilterFactory.CreateInstance will be called only one time during the whole ASP.NET Core application lifetime
public bool IsReusable
{
get
{
return false;
}
}
private class InternalAuthorizationFilter : IAuthorizationFilter
{
public InternalAuthorizationFilter()
{
//This InternalAuthorizationFilter constructor will be called per http request rather than ASP.NET Core application lifetime
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//OnAuthorization method will be called per http request
}
}
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
//Create InternalAuthorizationFilter instance per http request
return new InternalAuthorizationFilter();
}
}
}
Please be aware of IFilterFactory.IsReusable property, we need to return false, otherwise the IFilterFactory.CreateInstance method will be called only one time during ASP.NET Core application lifetime, and InternalAuthorizationFilter is still singleton.
Then, we need to specify the AuthorizationFilterWithFactoryAttribute on controller instead of InternalAuthorizationFilter, but AuthorizationFilterWithFactoryAttribute will actually create and operate on an InternalAuthorizationFilter instance per http request:
using Microsoft.AspNetCore.Mvc;
using AspNetCoreFilterDemo.Filters;
namespace AspNetCoreFilterDemo.Controllers
{
public class HomeController : Controller
{
public HomeController()
{
}
[AuthorizationFilterWithFactory]
public IActionResult Index()
{
return View();
}
}
}
AuthorizationFilterWithFactoryAttribute will still be singleton and created one time, but we approached the target to create the filter (InternalAuthorizationFilter) per http request.
You can also take a reference from MSDN.

WebApi's custom exception when "does not support http method"

I have a simple controller :
public class UsersController : ApiController
{
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Login([FromBody] UserLogin userLogin)
{
var userId = UserCleaner.Login(userLogin.MasterEntity, userLogin.UserName, userLogin.Password, userLogin.Ua);
if (userId == null) return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User not authorized");
return Request.CreateResponse(HttpStatusCode.OK, Functions.RequestSet(userId));
}
}
As you can see , only POST is currently available .
But when I invoke a GET in a browser (just for checking):
http://royipc.com:88/api/users
I get :
{"Message":"The requested resource does not support http method
'GET'."}
It is clear to me why it happens. But I want to return a custom exception when it happens.
Other answers here at SO doesn't show how I can treat this kind of situation (not that i've found of, anyway)
Question
How (and where) should I catch this kind of situation and return custom exception (HttpResponseMessage) ?
NB
I don't want to add a dummy GET method just for "catch and throw". tomorrow there can be a GET method. I just want to catch this Exception and return my OWN !
You may need to inherit from ApiControllerActionSelector class which is what the Web API uses to select the required action.
then you can replace the default IHttpActionSelector by your new action selector like that. config.Services.Replace(typeof(IHttpActionSelector), new MyActionSelector());
check this url for full example: http://www.strathweb.com/2013/01/magical-web-api-action-selector-http-verb-and-action-name-dispatching-in-a-single-controller/
You can build custom Exception filters in ASP.Net WebAPI. An exception filter is a class that implements the IExceptionFilter interface. To create a custom exception filter you can either implement the IExceptionFilter interface yourself or create a class that inherits from the inbuilt ExceptionFilterAttribute class. In the later approach all you need to do is override the OnException() method and plug-in some custom implementation.
public class MyExceptionFilter:ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An unhandled exception was thrown by the Web API controller."),
ReasonPhrase = "An unhandled exception was thrown by the Web API controller."
};
context.Response = msg;
}
}
you would likely want to test for conditions and generate the exact exception, but this is a bare example.
To use the exception class, you can either register it in the Global.asax, or as an attribute on a specific class or method.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new WebAPIExceptionsDemo.MyExceptionFilter());
AreaRegistration.RegisterAllAreas();
...
}
}
or
[MyExceptionFilter]
public class UsersController : ApiController
{
...
}

Servicestack - Order of Operations, Validation and Request Filters

I detected a problem in the RequestFilter execution order.
The ValidationFeature in ServiceStack is a Plugin that just registers a Global Request Filter. The Order of Operations points out that Global Request Filters are executed after Filter Attributes with a Priority <0 and before Filter Attributes with a Priority >=0
My BasicAuth filter has -100 priority, and in fact everything goes well if the Service is annotated at class level, but it fails when the annotation is at method level, with the authentication filter being executed after.
I am using 3.9.70
Is there any quick fix for this? Thanks
When you add the annotation at method level then you are creating an Action Request Filter (because you are adding the annotation to an action method) which in the Order of Operations is operation 8, after the other filters have run.
5: Request Filter Attributes with Priority < 0 gets executed
6: Then any Global Request Filters get executed
7: Followed by Request Filter Attributes with Priority >= 0
8: Action Request Filters (New API only)
The best workaround I can suggest is to reconsider your service structure. I imagine you are having these difficulties because you are adding unauthenticated api methods alongside your secure api methods, and thus are using method level attributes to control authentication. So you are presumably doing something like this Your classes and attributes will be different, this is just exemplar:
public class MyService : Service
{
// Unauthenticated API method
public object Get(GetPublicData request)
{
return {};
}
// Secure API method
[MyBasicAuth] // <- Checks user has permission to run this method
public object Get(GetSecureData request)
{
return {};
}
}
I would do this differently, and separate your insecure and secure methods into 2 services. So I use this:
// Wrap in an outer class, then you can still register AppHost with `typeof(MyService).Assembly`
public partial class MyService
{
public class MyPublicService : Service
{
public object Get(GetPublicData request)
{
return {};
}
}
[MyBasicAuth] // <- Check is now class level, can run as expected before Validation
public class MySecureService : Service
{
public object Get(GetSecureData request)
{
return {};
}
}
}
Solution - Deferred Validation:
You can solve your execution order problem by creating your own custom validation feature, which will allow you to defer the validation process. I have created a fully functional self hosted ServiceStack v3 application that demonstrates this.
Full source code here.
Essentially instead of adding the standard ValidationFeature plugin we implement a slightly modified version:
public class MyValidationFeature : IPlugin
{
static readonly ILog Log = LogManager.GetLogger(typeof(MyValidationFeature));
public void Register(IAppHost appHost)
{
// Registers to use your custom validation filter instead of the standard one.
if(!appHost.RequestFilters.Contains(MyValidationFilters.RequestFilter))
appHost.RequestFilters.Add(MyValidationFilters.RequestFilter);
}
}
public static class MyValidationFilters
{
public static void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
// Determine if the Request DTO type has a MyRoleAttribute.
// If it does not, run the validation normally. Otherwise defer doing that, it will happen after MyRoleAttribute.
if(!requestDto.GetType().HasAttribute<MyRoleAttribute>()){
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
return;
}
Console.WriteLine("Deferring Validation until Roles are checked");
}
}
Configure to use our plugin:
// Configure to use our custom Validation Feature (MyValidationFeature)
Plugins.Add(new MyValidationFeature());
Then we need to create our custom attribute. Your attribute will be different of course. The key thing you need to do is call ValidationFilters.RequestFilter(req, res, requestDto); if you are satisfied the user has the required role and meets your conditions.
public class MyRoleAttribute : RequestFilterAttribute
{
readonly string[] _roles;
public MyRoleAttribute(params string[] roles)
{
_roles = roles;
}
#region implemented abstract members of RequestFilterAttribute
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
Console.WriteLine("Checking for required role");
// Replace with your actual role checking code
var role = req.GetParam("role");
if(role == null || !_roles.Contains(role))
throw HttpError.Unauthorized("You don't have the correct role");
Console.WriteLine("Has required role");
// Perform the deferred validation
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
}
#endregion
}
For this to work we need to apply our custom attribute on the DTO route not the action method. So this will be slightly different to how you are doing it now, but should still be flexible.
[Route("/HaveChristmas", "GET")]
[MyRole("Santa","Rudolph","MrsClaus")] // Notice our custom MyRole attribute.
public class HaveChristmasRequest {}
[Route("/EasterEgg", "GET")]
[MyRole("Easterbunny")]
public class GetEasterEggRequest {}
[Route("/EinsteinsBirthday", "GET")]
public class EinsteinsBirthdayRequest {}
Then your service would look something like this:
public class TestController : Service
{
// Roles: Santa, Rudolph, MrsClaus
public object Get(HaveChristmasRequest request)
{
return new { Presents = "Toy Car, Teddy Bear, Xbox" };
}
// Roles: Easterbunny
public object Get(GetEasterEggRequest request)
{
return new { EasterEgg = "Chocolate" };
}
// No roles required
public object Get(EinsteinsBirthdayRequest request)
{
return new { Birthdate = new DateTime(1879, 3, 14) };
}
}
So when we call the route /EinsteinsBirthday which does not have a MyRole attribute the validation will be called normally, as if using the standard ValidationFeature.
If we call the route /HaveChristmas?role=Santa then our validation plugin will determine that the DTO has our attribute and not run. Then our attribute filter triggers and it will trigger the validation to run. Thus the order is correct.

Override a Global Filter in MVC for One Method

In my filterConfig, I have registered a global attribute filter, which requires authorization on each of my methods.
However, I have one particular method where I want to apply a different authorization filter attribute. How does one accomplish this, if at all possible?
Note: I do not want to use the [AllowAnonymous] attribute (which works seamlessly and completely ignores my filter), since I want the request to be authorized, just through a different set of authorization logic on the method.
You can alter your filter to allow multiple by setting AllowMultiple = true in the AttributeUsage attribute on your attribute class, and add a check so that if the filter is present multiple times, the globally-applied one doesn't execute. The ActionExecutingContext that gets passed into OnActionExecuting() lets you get the filters applied via filterContext.ActionDescriptor.GetCustomAttributes(), so you can use that here.
Then, alter the constructor so that you can pass in a parameter (probably an enum) that it can use to decide which authorisation method to use - the normal one, or this other one. Give that parameter a default value that makes it select the normal auth method. Then, on that one method that needs a different auth method, you can apply the filter with the other value of the parameter. So it might look like this:
public class CustomAuthAttribute : AuthorizeAttribute
{
public CustomAuthAttribute(AuthMethod method = AuthMethod.StandardAuth)
{
//stuff
}
}
[CustomAuth(AuthMethod.WeirdAuth)]
public ActionResult MethodThatNeedsDifferentAuth()
{
//stuff
}
you can write your own version of the authorize attribute and pass specific parameter to depending on what action would you like your attribute to do for example
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string currentAction { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (currentAction != "notallowed")
{
HandleUnauthorizedRequest(filterContext);
}
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
context.Result = new RedirectResult("/home/login");
}
and then apply it to your class or action
[CustomAuthorize(currentAction = "notallowed")]
public class HomeController : Controller

Categories