public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new System.Web.Mvc.AuthorizeAttribute()); }
ttribute in the ASP.NET Web API.
Custom Authorize Attribute
in ASP.NET WEB API you can extend "AuthorizeAttribute
" to implement custom authorization filter to control the access to the application. I have overridden the "OnAuthorization
" method to check custom authorization rules. In this implementation, I am assuming that user will send and receive the data through "HTTP headers".
Following is code example how to implement it.
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute { public override void OnAuthorization( System.Web.Http.Controllers.HttpActionContext actionContext) { base.OnAuthorization(actionContext); if (actionContext.Request.Headers.GetValues("authenticationToken") != null) { // get value from header string authenticationToken = Convert.ToString( actionContext.Request.Headers.GetValues("authenticationToken").FirstOrDefault()); //authenticationTokenPersistant // it is saved in some data store // i will compare the authenticationToken sent by client with // authenticationToken persist in database against specific user, and act accordingly if (authenticationTokenPersistant != authenticationToken) { HttpContext.Current.Response.AddHeader("authenticationToken", authenticationToken); HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized"); actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden); return; } HttpContext.Current.Response.AddHeader("authenticationToken", authenticationToken); HttpContext.Current.Response.AddHeader("AuthenticationStatus", "Authorized"); return; } actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed); actionContext.Response.ReasonPhrase = "Please provide valid inputs"; } }
Custom Handle Exception attribute:
To implement custom Handle Exception attribute you need to extend "ExceptionFilterAttribute
", and override "OnException
" method.
You can find the example below:
public class HandleExceptionAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) { if (actionExecutedContext.Exception != null) { var exception = actionExecutedContext.Exception; var response = new HttpResponseMessage(); response.StatusCode = HttpStatusCode.InternalServerError; response.ReasonPhrase = exception.Message; actionExecutedContext.Result = response; } } }
This article describes error and exception handling in ASP.NET Web API.
HttpResponseException
What happens if a Web API controller throws an uncaught exception? By default, most exceptions are translated into an HTTP response with status code 500, Internal Server Error.
The HttpResponseException type is a special case. This exception returns any HTTP status code that you specify in the exception constructor. For example, the following method returns 404, Not Found, if the id parameter is not valid.
publicProductGetProduct(int id){Product item = repository.Get(id);if(item ==null){thrownewHttpResponseException(HttpStatusCode.NotFound);}return item;}
For more control over the response, you can also construct the entire response message and include it with theHttpResponseException:
publicProductGetProduct(int id){Product item = repository.Get(id);if(item ==null){var resp =newHttpResponseMessage(HttpStatusCode.NotFound){Content=newStringContent(string.Format("No product with ID = {0}", id)),ReasonPhrase="Product ID Not Found"}thrownewHttpResponseException(resp);}return item;}
Exception Filters
You can customize how Web API handles exceptions by writing an exception filter. An exception filter is executed when a controller method throws any unhandled exception that is not an HttpResponseException exception. TheHttpResponseException type is a special case, because it is designed specifically for returning an HTTP response.
Exception filters implement the System.Web.Http.Filters.IExceptionFilter interface. The simplest way to write an exception filter is to derive from the System.Web.Http.Filters.ExceptionFilterAttribute class and override theOnException method.
Exception filters in ASP.NET Web API are similar to those in ASP.NET MVC. However, they are declared in a separate namespace and function separately. In particular, theHandleErrorAttribute class used in MVC does not handle exceptions thrown by Web API controllers.
Here is a filter that converts NotImplementedException exceptions into HTTP status code 501, Not Implemented:
namespaceProductStore.Filters{usingSystem;usingSystem.Net;usingSystem.Net.Http;usingSystem.Web.Http.Filters;publicclassNotImplExceptionFilterAttribute:ExceptionFilterAttribute{publicoverridevoidOnException(HttpActionExecutedContext context){if(context.ExceptionisNotImplementedException){ context.Response=newHttpResponseMessage(HttpStatusCode.NotImplemented);}}}}
The Response property of the HttpActionExecutedContext object contains the HTTP response message that will be sent to the client.
Registering Exception Filters
There are several ways to register a Web API exception filter:
- By action
- By controller
- Globally
To apply the filter to a specific action, add the filter as an attribute to the action:
publicclassProductsController:ApiController{[NotImplExceptionFilter]publicContactGetContact(int id){thrownewNotImplementedException("This method is not implemented");}}
To apply the filter to all of the actions on a controller, add the filter as an attribute to the controller class:
[NotImplExceptionFilter]publicclassProductsController:ApiController{// ...}
To apply the filter globally to all Web API controllers, add an instance of the filter to theGlobalConfiguration.Configuration.Filters collection. Exeption filters in this collection apply to any Web API controller action.
GlobalConfiguration.Configuration.Filters.Add(newProductStore.NotImplExceptionFilterAttribute());
If you use the "ASP.NET MVC 4 Web Application" project template to create your project, put your Web API configuration code inside the WebApiConfig
class, which is located in the App_Start folder:
publicstaticclassWebApiConfig{publicstaticvoidRegister(HttpConfiguration config){config.Filters.Add(newProductStore.NotImplExceptionFilterAttribute());// Other configuration code...}}
HttpError
The HttpError object provides a consistent way to return error information in the response body. The following example shows how to return HTTP status code 404 (Not Found) with an HttpError in the response body:
publicHttpResponseMessageGetProduct(int id){Product item = repository.Get(id);if(item ==null){var message =string.Format("Product with id = {0} not found", id);HttpError err =newHttpError(message);returnRequest.CreateResponse(HttpStatusCode.NotFound, err);}else{returnRequest.CreateResponse(HttpStatusCode.OK, item);}}
In this example, if the method is successful, it returns the product in the HTTP response. But if the requested product is not found, the HTTP response contains an HttpError in the request body. The response might look like the following:
HTTP/1.1404NotFoundContent-Type: application/json; charset=utf-8Date:Thu,09Aug201223:27:18 GMT Content-Length:51{"Message":"Product with id = 12 not found"}
Notice that the HttpError was serialized to JSON in this example. One advantage of using HttpError is that it goes through the same content-negotiation and serialization process as any other strongly-typed model.
Instead of creating the HttpError object directly, you can use the CreateErrorResponse method:
publicHttpResponseMessageGetProduct(int id){Product item = repository.Get(id);if(item ==null){var message =string.Format("Product with id = {0} not found", id);returnRequest.CreateErrorResponse(HttpStatusCode.NotFound, message);}else{returnRequest.CreateResponse(HttpStatusCode.OK, item);}}
CreateErrorResponse is an extension method defined in the System.Net.Http.HttpRequestMessageExtensionsclass. Internally, CreateErrorResponse creates an HttpError instance and then creates an HttpResponseMessagethat contains the HttpError.
HttpError and Model Validation
For model validation, you can pass the model state to CreateErrorResponse, to include the validation errors in the response:
publicHttpResponseMessagePostProduct(Product item){if(!ModelState.IsValid){returnRequest.CreateErrorResponse(HttpStatusCode.BadRequest,ModelState);}// Implementation not shown...}
This example might return the following response:
HTTP/1.1400BadRequestContent-Type: application/json; charset=utf-8Content-Length:320{"Message":"The request is invalid.","ModelState":{"item":["Required property 'Name' not found in JSON. Path '', line 1, position 14."],"item.Name":["The Name field is required."],"item.Price":["The field Price must be between 0 and 999."]}}
For more information about model validation, see Model Validation in ASP.NET Web API.
Adding Custom Key-Values to HttpError
The HttpError class is actually a key-value collection (it derives from Dictionary<string, object>), so you can add your own key-value pairs:
publicHttpResponseMessageGetProduct(int id){Product item = repository.Get(id);if(item ==null){var message =string.Format("Product with id = {0} not found", id);var err =newHttpError(message);err["error_sub_code"]=42;returnRequest.CreateErrorResponse(HttpStatusCode.NotFound, err);}else{returnRequest.CreateResponse(HttpStatusCode.OK, item);}}
Using HttpError with HttpResponseException
The previous examples return an HttpResponseMessage message from the controller action, but you can also useHttpResponseException to return an HttpError. This lets you return a strongly-typed model in the normal success case, while still returning HttpError if there is an error:
publicProductGetProduct(int id){Product item = repository.Get(id);if(item ==null){var message =string.Format("Product with id = {0} not found", id);thrownewHttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, message));}else{return item;}}