asp.net core middleware can't catch newtonsoft exception - c#

i use middleware catch request exception and write response like this
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
logger.LogError(ex.Message);
await HandleExceptionAsync(context, ex); //write response
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
FanjiaApiResultMessage resultMessage = new FanjiaApiResultMessage()
{
ResultCode = -1,
Data = null,
Msg = exception.Message
};
string result = JsonConvert.SerializeObject(resultMessage);
context.Response.ContentType = "application/json;charset=utf-8";
if (exception is QunarException)
{
context.Response.StatusCode = (int)(exception as QunarException).httpStatusCode;
}
else
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(result);
}
a request model param like this
public class FlightModel {
[JsonProperty("depCity", Required = Required.Always)]
public string DepCity { get; set; }
}
public IActionResult Test(FlightModel model){
return Content("test");
}
when i post the FlightModel without DepCity , i will get the exception
{
"errors": {
"": [
"Required property 'depCity' not found in JSON. Path '', line 6, position 1."
]
},
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "8000000a-0003-ff00-b63f-84710c7967bb"
}
Obviously the exception are not catched by middleware.
why middleware is not catch?

An Aspnet Core Model Validation failure does not throw an exception. It provides it's own response with a status code of 400 (Bad request) in a default format.
There are a few ways to override this including a custom attribute: https://www.jerriepelser.com/blog/validation-response-aspnet-core-webapi/
It looks like this:
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new ValidationFailedResult(context.ModelState);
}
}
}
And then is added like so:
[Route("api/values")]
[ValidateModel]
public class ValuesController : Controller
{
...
}
Or you can control the response generation by overriding the InvalidModelStateResponseFactory, like in this SO question: How do I customize ASP.Net Core model binding errors?
Here is an example:
services.Configure<ApiBehaviorOptions>(o =>
{
o.InvalidModelStateResponseFactory = actionContext =>
new MyCustomBadRequestObjectResult(actionContext.ModelState);
});

Related

Propagate exception message from service to controller .NET

I have a .NET appplication where there is a controller for receiving user requests, a service Service 1 which calls another service Service 2.
I have some code in the Service 2 where I query the database(DynamoDB) and get a 500 error in response when the user request values are incorrect. I want to handle this such that I catch this error/exception and send back the error message along with a 400 status code from the controller to the user. How should I modify the code to do this?
This is what I have tried. Currently, I'm just printing the error in Service 1 but I need to send it to the controller. Is sending the error message to the controller by throwing exceptions along the way the right way to do it?
The below code is similar to the actual code
Controller:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Service 1:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
Console.WriteLine(e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
Service 2:
private async Task<DynamoResult<TResponse>> GetPagedQueryResults(DynamoQuery query)
{
var results = new List<Document>();
try{
results = await search.GetNextSetAsync();
}
catch(Exception e){
throw new PaginationTokenException(e.Message);
}
return results;
}
[Serializable]
public class PaginationTokenException : Exception
{
public PaginationTokenException() { }
public PaginationTokenException(string message)
: base(message) {
throw new Exception(message);
}
public PaginationTokenException(string message, Exception inner)
: base(message, inner) { }
}
Assuming you want to hide implementation details from the controller (i.e. you don't want the controller to know/care that it's DynamoDB), I would create a custom exception and throw that from Service1.
Service1 would look something like this:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
throw new MyCustomException('My error message', e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
In the controller you can then capture that exception explicitly:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (MyCustomException e)
{
return BadRequest(e.Message);
}
}

How to return a value instead of a status if request is not authorised?

[authorise]
public string Get()
{
return "value1";
}
if I am not authorised it will return a status of 401 not authorised.
can it return a value such as json "{status:false,code:"401"}". ?
According to your description, I suggest you could try to use custommiddleware to achieve your requirement.
You could captured the 401 error in middleware and then rewrite the response body to {status:false,code:"401"}
More details, you could add below codes into Configure method above the app.UseAuthentication();:
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 401)
{
await context.Response.WriteAsync("{status:false,code:'401'}");
}
});
Result:
You can create a custom authorize attribute using IAsyncAuthorizationFilter.
public class CustomAuthorizeFilter : IAsyncAuthorizationFilter
{
public AuthorizationPolicy Policy { get; }
public CustomAuthorizeFilter(AuthorizationPolicy policy)
{
Policy = policy ?? throw new ArgumentNullException(nameof(policy));
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// Allow Anonymous skips all authorization
if (context.Filters.Any(item =&gt; item is IAllowAnonymousFilter))
{
return;
}
var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService&lt;IPolicyEvaluator&gt;();
var authenticateResult = await policyEvaluator.AuthenticateAsync(Policy, context.HttpContext);
var authorizeResult = await policyEvaluator.AuthorizeAsync(Policy, authenticateResult, context.HttpContext, context);
if (authorizeResult.Challenged)
{
// Return custom 401 result
context.Result = new CustomUnauthorizedResult("Authorization failed.");
}
else if (authorizeResult.Forbidden)
{
// Return default 403 result
context.Result = new ForbidResult(Policy.AuthenticationSchemes.ToArray());
}
}
}
public class CustomUnauthorizedResult : JsonResult
{
public CustomUnauthorizedResult(string message)
: base(new CustomError(message))
{
StatusCode = StatusCodes.Status401Unauthorized;
}
}
public class CustomError
{
public string Error { get; }
public CustomError(string message)
{
Error = message;
}
}
The code in this article does exactly what you want. click here
can it return a value such as json "{status:false,code:"401"}". ?
Sure, you can.
[ApiController]
[Produces("application/json")]
public class TestController : ControllerBase
{
public IActionResult Get()
{
if (User.Identity.IsAuthenticated)
{
return new OkObjectResult(new { status: true, code: 200 });
}
return new OkObjectResult(new { status: false, code: 401 });
}
}
But notice that, the request will return with the real status code 200 (OK)
You can also use UnauthorizedObjectResult like #vivek's comment:
return new UnauthorizedObjectResult(new { status: false, code: 401 });
You can return the below if using Asp.Net Core 3.1, It returns UnauthorizedObjectResult.
return Unauthorized(new { status: false, code: 401 });

How can i return status code in JsonResult [duplicate]

I was trying to return an error to the call to the controller as advised in
This link so that client can take appropriate action.
The controller is called by javascript via jquery AJAX. I am getting the Json object back only if I don't set the status to error.
Here is the sample code
if (response.errors.Length > 0)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);
I get the Json if I don't set the statuscode.
If I set the status code I get the status code back but not the Json error object.
Update
I want to send an Error object as JSON so that it can be handled error callback of ajax.
The neatest solution I've found is to create your own JsonResult that extends the original implementation and allows you to specify a HttpStatusCode:
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus)
{
Data = data;
_httpStatus = httpStatus;
}
public override void ExecuteResult(ControllerContext context)
{
context.RequestContext.HttpContext.Response.StatusCode = (int)_httpStatus;
base.ExecuteResult(context);
}
}
You can then use this in your controller action like so:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}
I found the solution here
I had to create a action filter to override the default behaviour of MVC
Here is my exception class
class ValidationException : ApplicationException
{
public JsonResult exceptionDetails;
public ValidationException(JsonResult exceptionDetails)
{
this.exceptionDetails = exceptionDetails;
}
public ValidationException(string message) : base(message) { }
public ValidationException(string message, Exception inner) : base(message, inner) { }
protected ValidationException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
Note that I have constructor which initializes my JSON. Here is the action filter
public class HandleUIExceptionAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = ((ValidationException)filterContext.Exception).myJsonError;
}
}
Now that I have the action filter, I will decorate my controller with the filter attribute
[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
var response = myClient.ValidateObject(objectToUpdate);
if (response.errors.Length > 0)
throw new ValidationException(Json(response));
}
When the error is thrown the action filter which implements IExceptionFilter get called and I get back the Json on the client on error callback.
There is a very elegant solution to this problem, just configure your site via web.config:
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>
Source: https://serverfault.com/questions/123729/iis-is-overriding-my-response-content-if-i-manually-set-the-response-statuscode
A simple way to send a error to Json is control Http Status Code of response object and set a custom error message.
Controller
public JsonResult Create(MyObject myObject)
{
//AllFine
return Json(new { IsCreated = True, Content = ViewGenerator(myObject));
//Use input may be wrong but nothing crashed
return Json(new { IsCreated = False, Content = ViewGenerator(myObject));
//Error
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new { IsCreated = false, ErrorMessage = 'My error message');
}
JS
$.ajax({
type: "POST",
dataType: "json",
url: "MyController/Create",
data: JSON.stringify(myObject),
success: function (result) {
if(result.IsCreated)
{
//... ALL FINE
}
else
{
//... Use input may be wrong but nothing crashed
}
},
error: function (error) {
alert("Error:" + erro.responseJSON.ErrorMessage ); //Error
}
});
Building on the answer from Richard Garside, here's the ASP.Net Core version
public class JsonErrorResult : JsonResult
{
private readonly HttpStatusCode _statusCode;
public JsonErrorResult(object json) : this(json, HttpStatusCode.InternalServerError)
{
}
public JsonErrorResult(object json, HttpStatusCode statusCode) : base(json)
{
_statusCode = statusCode;
}
public override void ExecuteResult(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
base.ExecuteResult(context);
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
return base.ExecuteResultAsync(context);
}
}
Then in your controller, return as follows:
// Set a json object to return. The status code defaults to 500
return new JsonErrorResult(new { message = "Sorry, an internal error occurred."});
// Or you can override the status code
return new JsonErrorResult(new { foo = "bar"}, HttpStatusCode.NotFound);
The thing that worked for me (and that I took from another stackoverflow response), is to set the flag:
Response.TrySkipIisCustomErrors = true;
You have to return JSON error object yourself after setting the StatusCode, like so ...
if (BadRequest)
{
Dictionary<string, object> error = new Dictionary<string, object>();
error.Add("ErrorCode", -1);
error.Add("ErrorMessage", "Something really bad happened");
return Json(error);
}
Another way is to have a JsonErrorModel and populate it
public class JsonErrorModel
{
public int ErrorCode { get; set;}
public string ErrorMessage { get; set; }
}
public ActionResult SomeMethod()
{
if (BadRequest)
{
var error = new JsonErrorModel
{
ErrorCode = -1,
ErrorMessage = "Something really bad happened"
};
return Json(error);
}
//Return valid response
}
Take a look at the answer here as well
You need to decide if you want "HTTP level error" (that what error codes are for) or "application level error" (that what your custom JSON response is for).
Most high level objects using HTTP will never look into response stream if error code set to something that is not 2xx (success range). In your case you are explicitly setting error code to failure (I think 403 or 500) and force XMLHttp object to ignore body of the response.
To fix - either handle error conditions on client side or not set error code and return JSON with error information (see Sbossb reply for details).
Several of the responses rely on an exception being thrown and having it handled in the OnException override. In my case, I wanted to return statuses such as bad request if the user, say, had passed in a bad ID. What works for me is to use the ControllerContext:
var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return jsonResult;
And if your needs aren't as complex as Sarath's you can get away with something even simpler:
[MyError]
public JsonResult Error(string objectToUpdate)
{
throw new Exception("ERROR!");
}
public class MyErrorAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message };
}
}
}
If you are just using MVC the simplest way is to use HttpStatusCodeResult.
public ActionResult MyAjaxRequest(string args)
{
string error_message = string.Empty;
try
{
// successful
return Json(args);
}
catch (Exception e)
{
error_message = e.Message;
}
return new HttpStatusCodeResult(500, error_message);
}
When the error is returned to the client you can display it or action it how you like.
request.fail(function (jqXHR) {
if (jqXHR.status == 500) {
alert(jqXHR.statusText);
}
})
I was running Asp.Net Web Api 5.2.7 and it looks like the JsonResult class has changed to use generics and an asynchronous execute method. I ended up altering Richard Garside's solution:
public class JsonHttpStatusResult<T> : JsonResult<T>
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(T content, JsonSerializerSettings serializer, Encoding encoding, ApiController controller, HttpStatusCode httpStatus)
: base(content, serializer, encoding, controller)
{
_httpStatus = httpStatus;
}
public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var returnTask = base.ExecuteAsync(cancellationToken);
returnTask.Result.StatusCode = HttpStatusCode.BadRequest;
return returnTask;
}
}
Following Richard's example, you could then use this class like this:
if(thereWereErrors)
{
var errorModel = new CustomErrorModel("There was an error");
return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}
Unfortunately, you can't use an anonymous type for the content, as you need to pass a concrete type (ex: CustomErrorType) to the JsonHttpStatusResult initializer. If you want to use anonymous types, or you just want to be really slick, you can build on this solution by subclassing ApiController to add an HttpStatusCode param to the Json methods :)
public abstract class MyApiController : ApiController
{
protected internal virtual JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings, Encoding encoding)
{
return new JsonHttpStatusResult<T>(content, httpStatus, serializerSettings, encoding, this);
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings)
{
return Json(content, httpStatus, serializerSettings, new UTF8Encoding());
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus)
{
return Json(content, httpStatus, new JsonSerializerSettings());
}
}
Then you can use it with an anonymous type like this:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return Json(errorModel, HttpStatusCode.InternalServerError);
}
Here is the JsonResult override answer for ASP.NET v5+ . I have tested and it works just as well as in earlier versions.
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus) : base(data)
{
_httpStatus = httpStatus;
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_httpStatus;
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var services = context.HttpContext.RequestServices;
var executor = services.GetRequiredService<IActionResultExecutor<JsonResult>>();
return executor.ExecuteAsync(context, this);
}
}

How to use customexceptionhandlers in ASP.net web-api

I am trying to understand custom exceptionhandlers but am not getting the hang of it. I tried implementing a custom exception handler like explained in the following pages:
https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling
Now my code is:
public class CustomRestErrorHandlerException : ExceptionHandler
{
public void CustomError(String error)
{
var message = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent("An unknown error occurred saying" + error)
};
throw new HttpResponseException(message);
}
public override void Handle(ExceptionHandlerContext context)
{
if (context.Exception is ArgumentNullException)
{
var result = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "ArgumentNullException"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentException)
{
var result = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is not found"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentOutOfRangeException)
{
var result = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is out of range"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else
{
CustomError(context.Exception.Message);
}
}
public class ErrorMessageResult : IHttpActionResult
{
private HttpRequestMessage _request;
private HttpResponseMessage _httpResponseMessage;
public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}
}
Then I try to call the exceptionhandler which I obviously do wrong : (this I probably do not understand <<
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new CustomRestErrorHandlerException(); << error occurs here
}
return this.Ok();
}
As you can see the error occurs because the exception is not an exceptionhandler but I have no idea how to then throw an exception through an custom exception handler since it's explained nowhere.
Can anyone explain this to me with a small example perhaps?
ExceptionHandler's have to be registered in the Web API configuration. This can be done in the WebApiConfig.cs file as shown below, where config is of type System.Web.Http.HttpConfiguration.
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
Once they are registered they are automatically called during unhandled exceptions. To test it out you might want to throw an exception in the action method such as:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new ArgumentNullException(); << error occurs here
}
return this.Ok();
}
You can now put a breakpoint in your exception handler and see how the unhandled exception is caught by the global ExceptionHandler.
Quote from: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/:
"The handler, like the logger, must be registered in the Web API configuration. Note that we can only have one Exception Handler per application."
config.Services.Replace(typeof(IExceptionHandler), new CustomRestErrorHandlerException ());
So add the line above to the WebApiConfig.cs file and then simply throw an exception from the controller:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
// do not use try catch here
//try
//{
// try clearing
//}
//catch(Exception e)
//{
//throw new CustomRestErrorHandlerException(); << error occurs here
//}
throw new Exception();
}

How can I consolidate all of my "setup code" away from my WebAPI controllers?

All of my controllers look like this:
[HttpPut]
[Route("api/businessname")]
[Authorize]
public HttpResponseMessage UpdateBusinessName(BusinessNameDto model) {
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
try {
_userService.UpdateBusinessName(User.Identity.Name, model.BusinessName);
return Request.CreateResponse(HttpStatusCode.OK, new ApiResponseDto() {});
} catch (Exception e) {
// logging code
//return Request.CreateResponse(HttpStatusCode.InternalServerError, e);
return Request.CreateResponse(HttpStatusCode.OK, new ApiResponseDto() { Success = false, Error = "Something bad happened :(" });
}
}
There's a lot of repeated stuff across my controllers. Ideally I could just have this:
[HttpPut]
[Route("api/businessname")]
[Authorize]
public HttpResponseMessage UpdateBusinessName(BusinessNameDto model) {
_userService.UpdateBusinessName(User.Identity.Name, model.BusinessName);
return Request.CreateResponse(HttpStatusCode.OK, new ApiResponseDto() {});
}
And tell WebAPI to do all that other stuff with every controller... but I don't know if that's possible. How can I make that happen?
You ca do the following:
1) Create a validation filter so that your action method executes ONLY if the model state is valid. So that you don't need to check ModelState.IsValid anymore in your action methods.
public class ValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
ModelStateDictionary modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, modelState);
}
}
}
2) Create a exception handling filter which will catch any exception thrown by the action method, serialize it and create a HTTP BadRequest response message to the client. So that you don't have to use try catch anymore in your action methods.
public class HandleExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var responseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest);
responseMessage.Content = new StringContent(context.Exception.Message);
context.Response = responseMessage;
}
}
You can register these filters in WebApiConfig.cs by adding the below lines to it
config.Filters.Add(new ValidationActionFilter());
config.Filters.Add(new HandleExceptionFilter());
UPDATE: To be more specific to SB2055's scenario, i am adding the below code
public class HandleExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var model = new ApiResponseDto() { Success = false, Error = context.Exception.Message })
context.Response = context.Request.CreateResponse(HttpStatusCode.OK,
model);
}
}
You can use ActionFilterAttributes. For example, to validate requests you can create a class similar to this:
public class ValidateRequestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)
}
}
}
You could then selectively apply the filter to your Web API actions by decorating them with the attribute:
[ValidateRequest]
[HttpPut]
[Route("api/businessname")]
[Authorize]
public HttpResponseMessage UpdateBusinessName(BusinessNameDto model) {
...
}
Or apply them to all your actions during Web API setup using HttpConfiguration.Filters:
config.Filters.Add(new ValidateRequestAttribute());

Categories