servicestackhttp-post-vars

Handling Serialization Exceptions in ServiceStack


I am using ServiceStack to create a service which accepts request from and HTML form (POSTed). One of the DTO properties is an Enum, and when the input doesn't match the Enum members, I get the following exception:

Error occured while Processing Request: KeyValueDataContractDeserializer: Error converting to type: Requested value 'MyValue' was not found.
System.Runtime.Serialization.SerializationException: KeyValueDataContractDeserializer: Error converting to type: Requested value 'MyValue' was not found. ---> System.ArgumentException: Requested value 'MyValue' was not found.
   at System.Enum.TryParseEnum(Type enumType, String value, Boolean ignoreCase, EnumResult& parseResult)
   at System.Enum.Parse(Type enumType, String value, Boolean ignoreCase)
   at ServiceStack.ServiceModel.Serialization.StringMapTypeDeserializer.PopulateFromMap(Object instance, IDictionary`2 keyValuePairs) 

How can I intercept this exception and handle it myself in my service code?


Solution

  • There are a couple of ways to handle this situation:

    You can make the DTO Enum property a string (since everything can successfully deserialize into a string :) and then convert that yourself manually i.e.

    using ServiceStack.Common; //ToEnum<> is an extension method
    
    public class RequestDto
    {
        public string EnumString { get; set; } 
    }
    
    public override object OnGet(RequestDto request)
    {
        MyEnum defaultValue = MyEnum.None;
        try {
            defaultValue = request.EnumString.ToEnum<MyEnum>();
        } catch {}
    }
    

    The other alternative is to completely remove it from the request DTO and get value manually from the IHttpRequest Context like:

    public class RequestDto {}
    public override object OnGet(RequestDto request)
    {
        MyEnum enumValue = MyEnum.DefaultValue;
        try {
            var enumStr = base.RequestContext.Get<IHttpRequest>().QueryString["EnumString"];
            enumValue = enumStr.ToEnum<MyEnum>();
        } catch {}
    }
    

    I generally discourage the use of enums on DTOs for many reasons, the primary one being on XML/SOAP endpoints the XSD treats them as a restricted set of values which is a pain in when trying iteratively to evolve your web services as you will need to re-gen the clients to add a new value.

    By convention the way I deal with it is to have all enums as strings but provide some metadata on the DTO which points to the target type (which helps in VS.NET/R# navigation and metadata tools).

    public class RequestDto
    {
        [References(typeof(MyEnum))]
        public string EnumString { get; set; } 
    }