简体   繁体   中英

WCF REST Service: WebInvoke with JSON and polymorphism does not work correctly

I have a typical WCF REST service in C# which accepts JSON input and returns a JSON output:

[ServiceContract]
public class WCFService
{
    [WebInvoke(Method = "POST", UriTemplate = "register", ResponseFormat = WebMessageFormat.Json)]
    public BasicResponse RegisterNewUser(UserDTO newUser)
    {
        return new BasicResponse()
        { status = "ERR_USER_NAME" };
    }
}

public class BasicResponse
{
    public string status { get; set; }
}

public class UserDTO
{
    public string username { get; set; }
    public string authCode { get; set; }
} 

This works as expected but I want to return different objects in case of normal execution and in case of error. I created a base response class and few inheritors. Now the WCF JSON serializer crashes and produces "400 Bad Request":

[ServiceContract]
public class WCFService
{
    [WebInvoke(Method = "POST", UriTemplate = "register", 
        ResponseFormat = WebMessageFormat.Json)]
    public BasicResponse RegisterNewUser(UserDTO newUser)
    {
        return new ErrorResponse()
        {
            status = "ERR_USER_NAME",
            errorMsg = "Invalid user name."
        };
    }
}

public class BasicResponse
{
    public string status { get; set; }
}

public class ErrorResponse : BasicResponse
{
    public string errorMsg { get; set; }
}

public class UserDTO
{
    public string username { get; set; }
    public string authCode { get; set; }
}

I tried to apply the [KnownType(typeof(ErrorResponse))] and [ServiceKnownType(typeof(ErrorResponse))] attributes without any success. Seems like a bug in the DataContractJsonSerializer which states it supports polymorphism.

My WCF REST service uses the WebServiceHostFactory:

<%@ ServiceHost Language="C#" Debug="true" 
    Service="WCFService" 
    CodeBehind="CryptoCharService.svc.cs"
    Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

In my Web.config I have standard HTTP endpoint:

<system.serviceModel>
  <standardEndpoints>
    <webHttpEndpoint>
      <standardEndpoint helpEnabled="true" defaultOutgoingResponseFormat="Json" />
    </webHttpEndpoint>
  </standardEndpoints>
</system.serviceModel>

Do you think this is fixable? I know a workaround (to return string and serialize the output manually) but why this does not work?

I found a way to partially overcome the described problem. When I need to return a normal value (eg BasicResponse), I just return it (my service returns BasicResponse object). When I need to return an error response, I return it as WebFaultException which is also serialized as JSON and is sent as HTTP response to the WCF service:

throw new WebFaultException<ErrorResponse>(
    new ErrorResponse() { errorMsg = "Error occured!" },
    HttpStatusCode.NotFound);

Now I can send the expected result as a normal method return value and any exceptional result in case of error through this WebFaultException.

ServiceKnownTypeAttribute works for me. Try this one:

[ServiceKnownType(typeof(ErrorResponse))] 
public BasicResponse RegisterNewUser(UserDTO newUser)
{
    return new ErrorResponse()
    {
        status = "ERR_USER_NAME",
        errorMsg = "Invalid user name."
    };
}

This should work correctly:

[DataContract]
[KnownType(typeof(ErrorResponse)]
public class BasicResponse
{
    [DataMember]
    public string status { get; set; }
}

[DataContract]
public class ErrorResponse : BasicResponse
{
    [DataMember]
    public string errorMsg { get; set; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM