2

I have a custom IModelBinder for my model:

public class MyBinder : IModelBinder {

public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) {
        // bla-bla-bla

        bindingContext.ModelState.AddModelError(
            bindingContext.ModelName, "Request value is invalid.");
        return false;
    }
}

I expect that when invalid value is passed in a request HTTP 400 Bad Request is returned automatically. However, this does not happen. What should I do to make Web API return HTTP 400 if there are any binding errors?

2

2 Answers 2

2

You can do as beautifulcoder suggests but that leaves much to be desired because you need to repeat that on every action. I would suggest your create an ActionFilterAttribute which onActionExecuting and onActionExecuted validates that the modelstate is valid and returns the BadRequest. You can then apply this to separate actions as [BadRequestIfModelNotValid] or to the global filters to have it applied to every requests.

public sealed class BadRequestIfModelNotValidAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
  /// <summary>
  /// if the modelstate is not valid before invoking the action return badrequest
  /// </summary>
  /// <param name="actionContext"></param>
  public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
  {
    var modelState = actionContext.ModelState;
    if (!modelState.IsValid)
      actionContext.Response = generateModelStateBadRequestResponse(modelState, actionContext.Request);

    base.OnActionExecuting(actionContext);//let other filters run if required
  }

  /// <summary>
  /// if the action has done additional modelstate checks which made it invalid we are going to replace the response with a badrequest
  /// </summary>
  /// <param name="actionExecutedContext"></param>
  public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
  {
    var modelState = actionExecutedContext.ActionContext.ModelState;
    if (!modelState.IsValid)
      actionExecutedContext.Response = generateModelStateBadRequestResponse(modelState, actionExecutedContext.Request);

    base.OnActionExecuted(actionExecutedContext);
  }

  private HttpResponseMessage generateModelStateBadRequestResponse(IEnumerable<KeyValuePair<string, ModelState>> modelState, HttpRequestMessage request)
  {
    var errors = modelState
      .Where(s => s.Value.Errors.Count > 0)
      .Select(s => new ApiErrorMessage {
          Parameter = s.Key,
          Message = getErrorMessage(s.Value.Errors.First())
        }) //custom class to normalize error responses from api
        .ToList();

    return request.CreateResponse(System.Net.HttpStatusCode.BadRequest, new ApiError
    {
      ExceptionType = typeof(ArgumentException).FullName,
      Messages = errors
    });
  }

  /// <summary>
  /// retrieve the error message or fallback to exception if possible
  /// </summary>
  /// <param name="modelError"></param>
  /// <returns></returns>
  private static string getErrorMessage(ModelError modelError)
  {
    if(!string.IsNullOrWhiteSpace(modelError.ErrorMessage))
      return modelError.ErrorMessage;

    if(modelError.Exception != null)
      return modelError.Exception.Message;

    return "unspecified error";
  }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Return it in your controller:

if (!ModelState.IsValid)
{
    return new HttpResponseMessage(HttpStatusCode.BadRequest);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.