My wcf webservice uses webhttpbinding(msdn) to expose a REST interface. Some methods accept an enum as parameter; e.g.:
[ServiceContract]
public partial interface IStrangeEnumDemo
{
[OperationContract]
[WebGet(UriTemplate = "DefaultEnumName?q={val}")]
string DefaultEnumName(DefaultNumberEnum val);
}
with DefaultNumberEnum
being:
// Note values in this Enum are not annotated with [System.RunTime.Serialization.EnumMember] or [System.Runtim.Serialization.DataContract], both are not needed
public enum DefaultNumberEnum
{
APPLE,
PEAR,
BANANA
}
The operation's implementation is:
public partial class StrangeEnumDemo : IStrangeEnumDemo
{
public string DefaultEnumName(DefaultNumberEnum val)
{
return $"Fruit: {val.ToString()}";
}
}
According to the MSDN documentation of QueryStringConverter
, Ohad Schneider's answer to Can I pass non-string to WCF RESTful service using UriTemplate?, and Carlos Figueira's MSDN blog the QueryStringConverter
should convert enum names to their value.
Calling the webservice via:
http://localhost:16772/StrangeEnumDemo.svc/DefaultEnumName?q=PEAR
results in the response1 as expected:
"Fruit: PEAR"
Another enum, that I would like to use as parameter, is backed by a byte
; e.g.:
public enum ByteEnum: byte
{
ALIGATOR,
BEAR,
ZEBRA
}
The operation declaration and implementation are almost identical to the ones of DefaultNumber
enum:
[ServiceContract]
public partial interface IStrangeEnumDemo
{
[OperationContract]
[WebGet(UriTemplate = "ByteEnumName?q={val}")]
string ByteEnumName(ByteEnum val);
}
and
public partial class StrangeEnumDemo : IStrangeEnumDemo
{
public string ByteEnumName(ByteEnum val)
{
return $"Animal: {val.ToString()}";
}
}
However, when calling:
http://localhost:16772/StrangeEnumDemo.svc/ByteEnumName?q=BEAR
I receive:
<Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Receiver</Value>
<Subcode>
<Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="nl-NL">Input string was not in a correct format.</Text>
</Reason>
<Detail>
<ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<HelpLink i:nil="true"/>
<InnerException i:nil="true"/>
<Message>Input string was not in a correct format.</Message>
<StackTrace> at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Byte.Parse(String s, NumberStyles style, NumberFormatInfo info)
at System.ServiceModel.Dispatcher.QueryStringConverter.ConvertStringToValue(String parameter, Type parameterType)
at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</StackTrace>
<Type>System.FormatException</Type>
</ExceptionDetail>
</Detail>
</Fault>
For completeness, this is my service.svc:
<%@ ServiceHost Language="C#" Debug="true" Service="StrangeEnumBehavior.StrangeEnumDemo" CodeBehind="StrangeEnumDemo.svc.cs" %>
And this is my web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
</appSettings>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.7" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.7"/>
<httpRuntime targetFramework="4.7"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior>
<webHttp automaticFormatSelectionEnabled="true" faultExceptionEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="webHttpBinding" scheme="http"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="false"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Is there a way to have QueryStringConverter
parse names of values of byte enums the same as it parses enums with implicit backing storage?
An solution I came up with is to use a different enum with implicit backing storage and use that a a data contract and convert manually (or use data contract surrogate to have automatic conversion); but I would rather not duplicate the enum.
The original enum has to be stored as a byte.
1I use the header Accept: application/json
to have the service return JSON data.