Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove JSON.net Serialization Exceptions from the ModelState

Problem Background

To save myself from duplicating validation logic I am following a pattern of pushing Server side ModelState errors to my View Model (MVVM KnockoutJS).

So by convention my Property Names on my KO ViewModel Match the Properties my Api is exposing and expecting, therefore I can map one to the other easily using a little Knockout plugin I wrote.

<validation-summary params="vm: $data, class: 'alert alert-error'"></validation-summary>

...

<div class="control-group" data-bind="errorCss: {'error': spend }">
     <label class="control-label" for="spend">Spend</label>
     <div class="controls">
        <div class="input-prepend">
           <span class="add-on">$</span>
           <input type="text" data-bind="value: spend" id="spend" class="input-medium" placeholder="Spend USD" />
         </div>   
          <validation-message params="bind: spend, class: 'text-error'"></validation-message>
      </div>
</div>

The Problem

Problem for me is that when JSON.Net serialises the JSON I send via and AJAX and when it encounters an exception it adds this to the ModelState as and Exception on the ModelError Class.

Example Response:

{
  "message": "The request is invalid.",
  "modelState": {
    "cmd.spend": [
      "Error converting value \"ii\" to type 'System.Double'. Path 'spend', line 1, position 13.",
      "'Spend' must be greater than '0'."
    ],
    "cmd.Title": [
      "'Title' should not be empty."
    ]
 }
}

The Problem Being that this message doesn't exactly give a great UX:

Error converting value "ii" to type 'System.Double'. Path 'spend', line 1, position 13.

And the fact that I am unable to separate this exception message from my validation messages as they are all in one Array.

I would prefer to remove this and handle this matter in my ValidationClass

I can remove them manually like so, and this is in a ActionFilter so I only have this once.

public class ValidateCommandAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            ModelStateDictionary modelState = actionContext.ModelState;

            #if !DEBUG
                for (int i = 0; i < modelState.Values.Count; i++)
                {
                    ModelErrorCollection errors = modelState.ElementAt(i).Value.Errors;
                    for (int i2 = 0; i2 < errors.Count; i2++)
                    {
                        ModelError error = errors.ElementAt(i2);
                        if (error.Exception != null)
                        {
                            // TODO: Add Log4Net Here
                            errors.RemoveAt(i2);
                        }
                    }
                }
            #endif

            if (!modelState.IsValid)
                actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, modelState); 
        }
    }

I know JSON.Net is highly configurable and wanted to know if there is somewhere in the API for it where I can turn this off, or suppress it?

like image 385
SimonGates Avatar asked Jul 16 '15 19:07

SimonGates


1 Answers

You can set an error handler. For example (from the json.net docs),

List<string> errors = new List<string>();

List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(@"[
      '2009-09-09T00:00:00Z',
      'I am not a date and will error!',
      [
        1
      ],
      '1977-02-20T00:00:00Z',
      null,
      '2000-12-01T00:00:00Z'
    ]",
    new JsonSerializerSettings
    {
        Error = delegate(object sender, ErrorEventArgs args)
        {
            errors.Add(args.ErrorContext.Error.Message);
            args.ErrorContext.Handled = true;
        },
        Converters = { new IsoDateTimeConverter() }
    });

// 2009-09-09T00:00:00Z
// 1977-02-20T00:00:00Z
// 2000-12-01T00:00:00Z

// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
// Unexpected token parsing date. Expected String, got StartArray.
// Cannot convert null value to System.DateTime.
like image 172
Jim Jeffries Avatar answered Sep 21 '22 08:09

Jim Jeffries