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?