1

I have specified a custom NonEmptyGuidAttribute which extends ValidationAttribute and looks like the following

public class NonEmptyGuidAttribute:ValidationAttribute
{
    public override bool IsValid(object value)
    {
        Guid parsedValue = Guid.Empty;
        if (value != null && Guid.TryParse(value.ToString(), out parsedValue))
        {
            return true;
        }

        return false;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        Guid parsedValue = Guid.Empty;
        if (value != null && Guid.TryParse(value.ToString(), out parsedValue) && parsedValue != Guid.Empty)
        {
            return ValidationResult.Success;
        }

        return new ValidationResult(
            this.ErrorMessage = string.Format("The value of {0} is not valid", validationContext.MemberName),
            new[] { validationContext.MemberName }
            );
    }
}

The purpose of this is just to make sure that if one of my models have a Guid property, that is is not empty and valid.

Below is a sample of this attribute being used. I have to make the Guid a nullable type so that the [Required] attribute is respected.

public class SomeNancyRequest
{
    [Required]
    [NonEmptyGuid]
    public Guid? Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string Description { get; set; }

    [Required]
    [DataType(DataType.ImageUrl)]
    public string ImagePath { get; set; }

    [Required]
    public bool Enabled {get;set;}
}

If I hit my API with a request such as the following, it does not utilize the [NonEmptyGuid] validation logic. I know this because I have placed breakpoints in it as well as a constructor to see if the attribute was even initialized.

{
    "id":"Im not a guid",
    "name": "Just a name",
    "description": "Some Description",
    "imagePath": "http://myimage.com",
    "enabled": true
}

I would expect this to hit my validator and fail due to the invalid id property in the request above. It DOES fail, but because an exceptionn is being thrown such as the following, when it tries to convert the Guid.

Nancy.RequestExecutionException: Oh noes! ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
   at System.Guid.GuidResult.SetFailure(ParseFailureKind failure, String failureMessageID, Object failureMessageFormatArgument, String failureArgumentName, Exception innerException)
   at System.Guid.TryParseGuidWithNoStyle(String guidString, GuidResult& result)
   at System.Guid.TryParseGuid(String g, GuidStyles flags, GuidResult& result)
   at System.Guid..ctor(String g)
   at System.ComponentModel.GuidConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at System.ComponentModel.NullableConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at System.ComponentModel.TypeConverter.ConvertFromInvariantString(String text)
   at Nancy.Json.JavaScriptSerializer.ConvertToType(Type type, Object obj)
   at Nancy.Json.JavaScriptSerializer.ConvertToObject(IDictionary`2 dict, Type type)
   at Nancy.Json.JavaScriptSerializer.ConvertToType(Type type, Object obj)
   at Nancy.Json.JavaScriptSerializer.ConvertToType[T](Object obj)
   at Nancy.Json.JavaScriptSerializer.Deserialize[T](String input)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Nancy.ModelBinding.DefaultBodyDeserializers.JsonBodyDeserializer.Deserialize(String contentType, Stream bodyStream, BindingContext context)
   at Nancy.ModelBinding.DefaultBinder.DeserializeRequestBody(BindingContext context)
   at Nancy.ModelBinding.DefaultBinder.Bind(NancyContext context, Type modelType, Object instance, BindingConfig configuration, String[] blackList)
   at Nancy.ModelBinding.DynamicModelBinderAdapter.TryConvert(ConvertBinder binder, Object& result)
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at Nancy.ModelBinding.ModuleExtensions.Bind[TModel](INancyModule module, String[] blacklistedProperties)
   at Nancy.ModelBinding.ModuleExtensions.BindAndValidate[TModel](INancyModule module)
   at Server.Extensibility.NancyModuleExtensions.d__3a`2.MoveNext() in c:\Users\Me\Source\Repos\my-server\.Server\Extensibility\NancyModuleExtensions.cs:line 121
   --- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Server.Extensibility.NancyModuleExtensions.<>c__DisplayClass25`2.<b__24>d__27.MoveNext() in c:\Users\Me\Source\Repos\my-server\Server\Extensibility\NancyModuleExtensions.cs:line 49
   --- End of inner exception stack trace ---
   at Nancy.NancyEngine.InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex)

Why is this not working?

TheJediCowboy
  • 8,924
  • 28
  • 136
  • 208
  • Raised an issue on GitHub https://github.com/NancyFx/Nancy/issues/2058 – Phill Sep 15 '15 at 06:54
  • Because this is a bug in the Mono implementation of the serializer we use in Nancy, we have opted not to fix it in 1.# release. The solution is to use a different serializer. – Phill Sep 25 '15 at 06:54

1 Answers1

2

Validation occurs after binding is done. So it looks like the internal JSON serializer is failing during conversion. So I would classify this as a bug. However you should be able to work around it by using the newtonsoft json library.

https://www.nuget.org/packages/Nancy.Serialization.JsonNet/

Install-Package Nancy.Serialization.JsonNet

You don't need to configure anything, it will automatically start using it when you install it.

albertjan
  • 7,739
  • 6
  • 44
  • 74
Phill
  • 18,398
  • 7
  • 62
  • 102