3

Say you've got a model that looks like

public class UserModel
{
    public string UserName { get; set; }

    public DateTime? DateOfBirth { get; set; }
}

The DateOfBirth field isn't required, but could be specified. You have a Web API POST endpoint that looks like

    [Route("")]
    [AcceptVerbs("POST")]
    public async Task<IHttpActionResult> Create(UserModel user)
    {

    }

And we've set the JSON serializer in start up like so,

    public static void Register(HttpConfiguration config)
    {
        var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();

        var settings = jsonFormatter.SerializerSettings;
        settings.Converters.Add(new IsoDateTimeConverter());

        settings.Error += (sender, args) => Console.WriteLine("This event is fired ok");
    }

If we send some JSON to the endpoint that looks like this

{
  "userName": "User1",
  "dateOfBirth": "jhdgjhjfg"
}

...the error event is fired in the Serializer settings and the endpoint is called. At this point, the DateOfBirth field is null and I don't have any context that a deserialization error has occurred

Reading the JSON.Net documentation, because Handled == false in the Error event arguments of the Settings object, an exception should be raised into the application code - this doesn't happen? Is there a setting I haven't configured correctly for this?

How can I get context within the action so that I know a value was specified for a field and couldn't be deserialized? Even global behaviour would be fine, as long as I know this has happened and can return a 400.

UPDATE:

We can use a filter to check the Model state, then check the Model State errors for exceptions of type JsonReaderException. This lets you return a 400 with a list of violating fields

public class CheckJsonExceptionModelStateAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ModelState.IsValid)
        {
            return;
        }

        var fieldsInError = new List<string>();

        foreach (var jsonException in 
            actionContext.ModelState.Keys.SelectMany(key => actionContext.ModelState[key].Errors)
            .Select(error => error.Exception).OfType<JsonReaderException>())
        {
            Trace.TraceError(jsonException.Message);
            fieldsInError.Add(jsonException.Path);
        }

        var apiError = new { ErrorMessages.BadRequestModel.Message, FieldsInError = new List<string>() };

        foreach (var fieldError in fieldsInError)
        {
            apiError.FieldsInError.Add(fieldError);
        }

        actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, apiError);
    }
}

1 Answer 1

1

You have multiple options. But first, you are getting no exception because the WebApi handles this exception. Bad news.

Good news, you can handle it in at least two ways; use the ModelState.IsValid property - in your case it will be false. You can access them in your post-method. When you remove the invalid dateOfBirth it is true ;-)

Or you can use an ActionFilterAttribute to put it on your methods for re-use purposes.

For example:

public async Task<IHttpActionResult> Create(UserModel user) {
        if (!ModelState.IsValid) {
             // ModelState.Keys // Get all error-keys
        }
}
Sign up to request clarification or add additional context in comments.

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.