Terminate the execution of web api request in Initialize function - c#

I am working on some Restful APIs in .net web api . All the API controllers that I am working on inherit from a base API controller. It has some logic in the Initialize function.
protected override void Initialize(HttpControllerContext controllerContext)
{
// some logic
}
There is a new product requirement comes in and I want to return the response to the client in the Initialize function depending on some criteria.
e.g.
protected override void Initialize(HttpControllerContext controllerContext)
{
// some logic
controllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "error");
}
however it seems like that the .net pipeline still moves on even I return the response already.
Is there anyway to return the response inside that function and stop the execution? Or I have to refactor the existing code to do it in another way?

Here is a hacky way of accomplishing what you want. Throw an exception like so.
protected override void Initialize(HttpControllerContext controllerContext)
{
// some logic
if(youhavetosend401)
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
The cleaner way, assuming what you are trying to do is all about authorization is to create an authorization filter like so.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext context)
{
// Do your stuff and determine if the request can proceed further or not
// If not, return false
return true;
}
}
Apply the filter on the action method or controller or even globally.
[MyAuthorize]
public HttpResponseMessage Get(int id)
{
return null;
}

Use HttpResponseException to send the HttpResponseMessage created for Error.
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
//Your Logic
throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, "error"));
base.Initialize(controllerContext);
}

Use
Application.CompleteRequest()
it will fire the EndRequest event.

Related

How to prevent custom filters twice calls?

I have a custom authorize filter that called two times.
public sealed class SamaAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//some code
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//some logic code here
return true;
}
}
I just add it globally and not set it for any action.
i search it in all web but can not find good answer.
i used StructureMap library for dependency injection;
but i don't have any ioc filter registration.
even i used "AllowMultiple = false" but not worked for me.
the problem was an ajax call in my scripts.

Add custom header to all responses in Web API

Simple question, and I am sure it has a simple answer but I can't find it.
I am using WebAPI and I would like to send back a custom header to all responses (server date/time requested by a dev for syncing purposes).
I am currently struggling to find a clear example of how, in one place (via the global.asax or another central location) I can get a custom header to appear for all responses.
Answer accepted, here is my filter (pretty much the same) and the line i added to the Register function of the WebApi config.
NOTE: The DateTime stuff is NodaTime, no real reason just was interested in looking at it.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("ServerTime", Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()).ToString());
}
Config Line:
config.Filters.Add(new ServerTimeHeaderFilter());
For that you can use a custom ActionFilter (System.Web.Http.Filters)
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
}
}
You can then apply the filter to all your controller's actions by adding this in the configuration in Global.asax for example :
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());
You can also apply the filter attribute to the action that you want without the global cofiguration line.
Previous answers to this question don't address what to do if your controller action throws an exception. There are two basic ways to get that to work:
Add an exception filter:
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class HeaderAdderExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
context.Response.Content.Headers.Add("header", "value");
}
}
and in your WebApi setup:
configuration.Filters.Add(new HeaderAdderExceptionFilter());
This approach works because WebApi's default exception handler will send the HttpResponseMessage created in a filter instead of building its own.
Replace the default exception handler:
using System.Net;
using System.Net.Http;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
public class HeaderAdderExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
HttpResponseMessage response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
response.Headers.Add("header", "value");
context.Result = new ResponseMessageResult(response);
}
}
and in your WebApi setup:
configuration.Services.Replace(typeof(IExceptionHandler), new HeaderAdderExceptionHandler());
You can't use both of these together. Okay, well, you can, but the handler will never do anything because the filter already converted the exception into a response.
Super important to note that as written, this code will send all the exception details to the client. You probably don't want to do this in production, so check out all the available overloads on CreateErrorResponse() and pick which one suits your needs.
Julian's answer led me to have to create the filter but only using the the System.Web (v4) and System.Web.Http (v5) namespace (MVC packages were not part of this particular project this was used on.)
using System.Web;
using System.Web.Http.Filters;
...
public class AddCustomHeaderActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
actionExecutedContext.ActionContext.Response.Headers.Add("name", "value");
}
}
And add it to the global.asax to have it used on every controller/action
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());
Neither of the above two solutions worked for me. They wouldn't even compile. Here's what I did. Added:
filters.Add(new AddCustomHeaderFilter());
to RegisterGlobalFilters(GlobalFilterCollection filters) method in FiltersConfig.cs and then added
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
actionExecutedContext.HttpContext.Response.Headers.Add("ServerTime", DateTime.Now.ToString());
}
}
It can be done by the messagehandler easily, it will handle both ok response and exception case.
public class CustomHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// add header to request if you want
var response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("cutomKey", "cutomValue");
return response;
}
}
Add it in the config
config.MessageHandlers.Add(new CustomHeaderHandler());
According to my requirement, below single line of code serves the purpose.
System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")
I combined the normal and exception path in one class:
public class CustomHeaderAttribute : FilterAttribute, IActionFilter, IExceptionFilter
{
private static string HEADER_KEY { get { return "X-CustomHeader"; } }
private static string HEADER_VALUE { get { return "Custom header value"; } }
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return (new CustomHeaderAction() as IActionFilter).ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
}
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
return (new CustomHeaderException() as IExceptionFilter).ExecuteExceptionFilterAsync(actionExecutedContext, cancellationToken);
}
private class CustomHeaderAction: ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
{
actionExecutedContext.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
private class CustomHeaderException : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
}
context.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
Nothing fancy but at least it gives me one place to control my additional headers. For now it's just static content but you could always hook it up to some sort of dictionary generator/factory.
I had the same problem while trying to add a new header to the whole controller, just add "services.AddHttpContextAccessor();" to startup.cs then create your controller
public class EnController : Controller{
public EnController(IHttpContextAccessor myHttpAccessor)
{
myHttpAccessor.HttpContext.Response.Headers.Add("Content-Language", "en-US");
}
... more methods here...
}

Custom authorizations in Web.API

My understanding of ASP.NET MVC is that for authorizations I should use something like -
public class IPAuthorize : AuthorizeAttribute {
protected override bool AuthorizeCore(HttpContextBase httpContext) {
//figure out if the ip is authorized
//and return true or false
}
But in Web API, there is no AuthorizeCore(..).
There is OnAuthorization(..) and the general advice for MVC is not to use OnAuthorization(..).
What should I use for custom authorizations in Web API?
Authorization is done in an authorization filter - that mean you derive from System.Web.Http.AuthorizeAttribute and implement the IsAuthorized method.
You don't implement authorization in a normal action filter because they run later in the pipeline than authorization filters.
You also don't implement authentication in a filter (like parsing a JWT) - this is done even earlier in an extensibility point called MessageHandler.
The method we use for is an custom ApiAuthorize attribute that inherits from System.Web.Http.AuthorizeAttribute. for example:
public class ApiAuthorizeAttribute : AuthorizeAttribute
{
readonly CreditPointModelContext _ctx = new CreditPointModelContext();
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if(Authorize(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
throw new HttpResponseException(challengeMessage);
}
private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext)
{
try
{
//boolean logic to determine if you are authorized.
//We check for a valid token in the request header or cookie.
}
catch (Exception)
{
return false;
}
}
}

Get attributes for method given by name in ASP.NET MVC 4

I'm using ravendb as storage backend. Since it uses unit of work pattern I need to open session, perform actions, save results and close session. I want to keep my code clean and don't call session opening and closing explicitly in each action, so I put this code to OnActionExecuting and OnActionExecuted methods, like this:
#region RavenDB's specifics
public IDocumentSession DocumentSession { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
this.DocumentSession = Storage.Instance.OpenSession();
base.OnActionExecuting(filterContext);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
if (this.DocumentSession != null && filterContext.Exception == null)
{
this.DocumentSession.SaveChanges();
}
this.DocumentSession.Dispose();
base.OnActionExecuted(filterContext);
}
#endregion
But some actions require connection to ravendb and come don't. So I've decided to create custom attribute and mark methods need to have DocumentSession opened with it. Here is an example:
//
// GET: /Create
[DataAccess]
public ActionResult Create()
{
return View();
}
And I stuck. My plan was to retrieve actions' attributes in the OnActionExecuted method and if [DataAccess] is present, open DocumentSession.
In the OnActionExecuted I can retrieve action name (method's name) via filterContext.ActionDescriptor.ActionName statement. But how I can retrieve method's attributes of the given class using reflection?
I found out that it might be Attribute.GetCustomAttributes call, but closest I got — I need to have MemberInfo object of the method. But how I can get this MemberInfo for method given by name?
If you inherit your custom attribute from FilterAttribute, it will have OnActionExecuted and OnActionExecuting methods. And it will be executed before general OnActionExecuted and OnActionExecuting.
Example:
public class DataAccessAttribute: FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
var controller = (YourControllerType)filterContext.Controller;
controller.DocumentSession = Storage.Instance.OpenSession();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
var controller = (YourControllerType)filterContext.Controller;
documentSession = controller.DocumentSession;
if (documentSession != null && filterContext.Exception == null)
{
documentSession.SaveChanges();
}
documentSession.Dispose();
}
Why not have your DataAccess attribute inherit from ActionFilterAttribute so you can put the ActionExecuting/Executed methods on the attribute instead of the Controller?
Example of how to do it with NHibernate by using an action filter to set the session on a base controller. It's done using NHibernate, but very similar to what you'd need to do and it's written by Ayende who's one of the RavenDB authors I believe.

How do you return something in a ActionFilterAttribute?

I am trying to use the ActionFilterAttribute in asp.net mvc. What I want to do is check if the users subscription is expired if it is I want to redirect the user to a page if not then do nothing.
But the methods that you can override are all void. So how do I do this? Like I don't even know how to unit test it either since it is void.
public override void OnActionExecuting(ActionExecutingContext filterContext)
I think what you want is an Authorization Filter:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (...subscriptionExpired...)
{
filterContext.Cancel = true;
filterContext.Result = new RedirectResult("/user/login");
}
}

Categories

Resources