1

I have a post route that accepts some JSON payload in the request body.

Post["/myroute/}"] = _ =>
{
    try
    {
       var model = this.Bind<MyModel>();
    }
    catch (ModelBindingException e)  
    {
       //PropertyBindException list is empty here, 
       //so only the first exception can be handled...
    }
}

If there are multiple invalid data types (i.e. if there are several int properties defined in MyModel, and a user posts strings for those properties), I would like pass back a nice list of these errors, similar to how would use ModelState dictionary in a vanilla ASP.NET MVC application.

How can I accomplish this type of exception handling when attempting to bind the JSON payload in the request body to my Model in NancyFX?

Update:

Looking through the DefaultBinder in the Nancy source here: https://github.com/sloncho/Nancy/blob/master/src/Nancy/ModelBinding/DefaultBinder.cs

The problem I see is that in this block:

        try
        {
            var bodyDeserializedModel = this.DeserializeRequestBody(bindingContext);
            if (bodyDeserializedModel != null)
            {
                UpdateModelWithDeserializedModel(bodyDeserializedModel, bindingContext);
            }
        }
        catch (Exception exception)
        {
            if (!bindingContext.Configuration.IgnoreErrors)
            {
                throw new ModelBindingException(modelType, innerException: exception);
            }
        }

The Deserialize call seems to be "all or nothing" and it is handled by a plain Exception, not a ModelBindException, so I cannot see any PropertyBindExceptions here either.

Should I be needing to implement something custom for this...?

Josh
  • 451
  • 8
  • 22
  • I'm running into the same issue. https://stackoverflow.com/questions/26107656/ignore-parsing-errors-during-json-net-data-parsing shows an easy way to ignore individual parsing errors, and you could even trap them individually. So it appears to me that you need to implement your own IBodyDeserializer utilizing this method. – Tim Feb 16 '18 at 16:18

1 Answers1

0

Add your own custom body serializer that uses newtonsoft to ignore errors:

 public class CustomBodyDeserializer : IBodyDeserializer
{
    private readonly MethodInfo deserializeMethod = typeof(JavaScriptSerializer).GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.Public);
    private readonly JsonConfiguration jsonConfiguration;
    private readonly GlobalizationConfiguration globalizationConfiguration;

    /// <summary>
    /// Initializes a new instance of the <see cref="JsonBodyDeserializer"/>,
    /// with the provided <paramref name="environment"/>.
    /// </summary>
    /// <param name="environment">An <see cref="INancyEnvironment"/> instance.</param>
    public CustomBodyDeserializer(INancyEnvironment environment)
    {
        this.jsonConfiguration = environment.GetValue<JsonConfiguration>();
        this.globalizationConfiguration = environment.GetValue<GlobalizationConfiguration>();
    }

    /// <summary>
    /// Whether the deserializer can deserialize the content type
    /// </summary>
    /// <param name="mediaRange">Content type to deserialize</param>
    /// <param name="context">Current <see cref="BindingContext"/>.</param>
    /// <returns>True if supported, false otherwise</returns>
    public bool CanDeserialize(MediaRange mediaRange, BindingContext context)
    {
        return Json.IsJsonContentType(mediaRange);
    }

    /// <summary>
    /// Deserialize the request body to a model
    /// </summary>
    /// <param name="mediaRange">Content type to deserialize</param>
    /// <param name="bodyStream">Request body stream</param>
    /// <param name="context">Current context</param>
    /// <returns>Model instance</returns>
    public object Deserialize(MediaRange mediaRange, Stream bodyStream, BindingContext context)
    {            

        //var serializer = new JavaScriptSerializer(this.jsonConfiguration, this.globalizationConfiguration);

        //serializer.RegisterConverters(this.jsonConfiguration.Converters, this.jsonConfiguration.PrimitiveConverters);

        if (bodyStream.CanSeek)
        {
            bodyStream.Position = 0;
        }

        string bodyText;
        using (var bodyReader = new StreamReader(bodyStream))
        {
            bodyText = bodyReader.ReadToEnd();
        }

       // var genericDeserializeMethod = this.deserializeMethod.MakeGenericMethod(context.DestinationType);

       // var deserializedObject = genericDeserializeMethod.Invoke(serializer, new object[] { bodyText });


        object deserializedObject = JsonConvert.DeserializeObject(bodyText, context.DestinationType, new JsonSerializerSettings
        {
            Error = HandleDeserializationError
        });

        return deserializedObject;
    }

    public void HandleDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorArgs)
    {
        string currentError = errorArgs.ErrorContext.Error.Message;
        errorArgs.ErrorContext.Handled = true;
    }
}
Tim
  • 606
  • 4
  • 7