开发者

WCF JSON Service returns XML on Fault

开发者 https://www.devze.com 2023-03-14 20:32 出处:网络
I am running a ServiceHost to test one of my services and all works fine until I throw a FaultException - bang I get XML not JSON

I am running a ServiceHost to test one of my services and all works fine until I throw a FaultException - bang I get XML not JSON

my service contract - lovely

   /// <summary>
    ///   <para>Get category by id</para>
    /// </summary>
    [OperationContract(AsyncPattern = true)]
    [FaultContract(typeof(CategoryNotFound))]
    [FaultContract(typeof(UnexpectedExceptionDetail))]
    IAsyncResult BeginCategoryById(
        CategoryByIdRequest request,
        AsyncCallback callback, object state);

    CategoryByIdResponse EndCategoryById(IAsyncResult result);

Host Set-up - scrummy yum

var host = new ServiceHost(serviceType, new Uri(serviceUrl));
host.AddServiceEndpoint(
    serviceContract,
    new WebHttpBinding(), "")
        .Behaviors.Add(
             new WebHttpBehavior
                            {
                                DefaultBodyStyle = WebMessageBodyStyle.Bare,
                                DefaultOutgoingResponseFormat = WebMessageFormat.Json,
                                FaultExceptionEnabled = true
                            });

host.Open();

Here's the call - oo belly ache

var request = WebRequest.Create(serviceUrl + "/" + serviceName);
request.Method = "POST";
request.ContentType = "application/json; charset=utf-8";
request.ContentLength = 0;

try
{
    // receive response
    using (var response = request.GetResponse())
    {
        var responseStream = response.GetResponseStream();

        // convert back into referenced object for verification
        var deserialiser = new DataContractJsonSerializer(typeof (TResponseData));
        return (TResponseData) deserialiser.ReadObject(responseStream);
    }
}
catch (WebExcep开发者_Go百科tion wex)
{
    var response = wex.Response;

    using (var responseStream = response.GetResponseStream())
    {
        // convert back into fault
        //var deserialiser = new DataContractJsonSerializer(typeof(FaultException<CategoryNotFound>));
        //var fex = (FaultException<CategoryNotFound>)deserialiser.ReadObject(responseStream);

        var text = new StreamReader(responseStream).ReadToEnd();
        var fex = new Exception(text, wex);    

        Logger.Error(fex);
        throw fex;
    }
}

the text var contains the correct fault, but serialized as Xml What have I done wrong here?


The answer is to implement an IErrorHandler and supporting behavior

I found this excellent post by iainjmitchell

http://iainjmitchell.com/blog/?p=142


I can happily present the solution. I had exactly the same problem and after i messed a little with my endpoint behavior configuration i discovered the needed config element. The solution is to force wcf to use the selected format (json):

 <behavior name="ExtendedJSONBehavior">
     <webHttp defaultOutgoingResponseFormat="Json" defaultBodyStyle="Wrapped" automaticFormatSelectionEnabled="false"/>
 </behavior>

As you can see, the key was the "automaticFormatSelectionEnabled" attribute.

Have fun with wcf again


This probably won't give you the "why" part. Take a look a this question. It lets you grab and reformat the faults before they go out as a response. This will at least Json-ize your responses enough to get you going. this also has a similar idea behind it.


According to MSDN documentation for DataContractJsonSerializer:

"If an error occurs during the serialization of an outgoing reply on the server or the reply operation throws an exception for some other reason, it may not get returned to the client as a fault."

Also, and this is mere speculation, but it almost looks like this serializer serializes to XML and then converts it to JSON. So when your Fault happens, it gets interrupted mid-process? Then again I could be totally wrong.

Good luck.


I don't understand why you are using WebRequest to make calls to a WCF service. Is there a specific reason for this? How do you know when you handle that WebException that it will be a FaultException<CategoryNotFound>?
If you use a service proxy, and your service throws FaultException<T>, it's probably better to write your try-catch like this:

try
{
    //Do service call
} 
catch (FaultException<CategoryNotFound> fe)
{
    //handle CategoryNotFound
}
catch (FaultException<UnexpectedExceptionDetail> fe)
{
    //handle UnexpectedExceptionDetail
}
catch (FaultException exc)
{
    //stuf
}
catch(Exception general){
    //all other stuff that might blow up
}


//由于调用 ProvideFault 时,客户端处于阻塞状态,不要在这里进行长时间的操作
public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
{
    //避免敏感信息泄漏,例如:数据库配置, error包含的错误信息应该记录到服务器的日志中,不能显示给客户端
    // FaultException<int> e = new FaultException<int>(123, error.Message);
    DateTime now = DateTime.Now;
    time = now.ToString("yyyyMMddHHmmssfff", DateTimeFormatInfo.InvariantInfo);// "" + now.Year.ToString() + now.Month.ToString() + now.Day.ToString() + now.Hour.ToString() + now.Minute.ToString() + now.Second.ToString() + now.Millisecond.ToString();
    string errorMsg = "服务内部错误_" + time;
    // FaultException fe = new FaultException(errorMsg);
    // MessageFault mf = fe.CreateMessageFault();
    // msg = Message.CreateMessage(version, mf, fe.Action);

    //The fault to be returned
    msg = Message.CreateMessage(version, "", errorMsg, new DataContractJsonSerializer(typeof(string)));

    // tell WCF to use JSON encoding rather than default XML
    WebBodyFormatMessageProperty wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);

    // Add the formatter to the fault
    msg.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

    //Modify response
    HttpResponseMessageProperty rmp = new HttpResponseMessageProperty();

    // return custom error code, 400.
    rmp.StatusCode = System.Net.HttpStatusCode.InternalServerError;
    rmp.StatusDescription = "Bad request";

    //Mark the jsonerror and json content
    rmp.Headers[HttpResponseHeader.ContentType] = "application/json";
    rmp.Headers["jsonerror"] = "true";

    //Add to fault
    msg.Properties.Add(HttpResponseMessageProperty.Name, rmp);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消