POST to WCF from Fiddler succeeds but passes null

2020-02-29 10:26发布

I've followed this video series on WCF and have the demo working. It involves building a WCF service that manages student evaluation forms, and implements CRUD operations to operate on a list of those evaluations. I've modified my code a little according to this guide so it will return JSON results to a browser or Fiddler request. My goal is to figure out how to use the service by building my own requests in Fiddler, and then use that format to consume the service from an app on a mobile device.

I'm having trouble using the SubmitEval method (save an evaluation) from Fiddler. The call works, but all the fields of Eval are null (or default) except Id, which is set in the service itself.

Here is my Eval declaration (using properties instead of fields as per this question):

[DataContract]
public class Eval //Models an evaluation
{
    [DataMember]
    public string Id { get; set; }

    [DataMember]
    public string Submitter { get; set; }

    [DataMember]
    public string Comment { get; set; }

    [DataMember]
    public DateTime TimeSubmitted { get; set; }
}

And here is the relevant part of IEvalService:

[ServiceContract]
public interface IEvalService
{
 ...
   [OperationContract]
    [WebInvoke(RequestFormat=WebMessageFormat.Json,
       ResponseFormat=WebMessageFormat.Json, UriTemplate = "eval")]
    void SubmitEval(Eval eval);
}

And EvalService:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class EvalService : IEvalService
{
...
  public void SubmitEval(Eval eval)
    {
        eval.Id = Guid.NewGuid().ToString();
        Console.WriteLine("Received eval");
        evals.Add(eval);
    }
}

In Fiddler's Request Builder, I set the Method to POST and the address to http://localhost:49444/EvalServiceSite/Eval.svc/eval. I added the header Content-Type: application/json; charset=utf-8 to the default headers. The request body is:

{"eval":{"Comment":"testComment222","Id":"doesntMatter", 
  "Submitter":"Tom","TimeSubmitted":"11/10/2011 4:00 PM"}}

The response I get from sending that request is 200, but when I then look at the Evals that have been saved, the one I just added has a valid Id, but Comment and Submitter are both null, and TimeSubmitted is 1/1/0001 12:00:00 AM.

It seems like WCF is getting the request, but is not deserializing the object correctly. But if that's the case, I don't know why it's not throwing some sort of exception. Does it look like I'm doing this right, or am I missing something?

UPDATE: Here is the endpoint declaration in App.config:

 <endpoint binding="webHttpBinding" behaviorConfiguration="webHttp"
   contract="EvalServiceLibrary.IEvalService" />

And the endpoint behavior referred to:

<behavior name="webHttp">
  <webHttp defaultOutgoingResponseFormat="Json"/>
</behavior>

标签: wcf json Fiddler
2条回答
放我归山
2楼-- · 2020-02-29 10:50

The default body style in the WebInvoke attribute is Bare, meaning that the object should be sent without a wrapper containing the object name:

{"Comment":"testComment222",
 "Id":"doesntMatter",
 "Submitter":"Tom",
 "TimeSubmitted":"11/10/2011 4:00 PM"}

Or you can set the request body style to Wrapped, and that would make the input require the wrapping {"eval"...} object:

[OperationContract]
[WebInvoke(RequestFormat=WebMessageFormat.Json,
   ResponseFormat=WebMessageFormat.Json,
   UriTemplate = "eval",
   BodyStyle = WebMessageBodyStyle.WrappedRequest)] // or .Wrapped
void SubmitEval(Eval eval);

Update: there's another problem in your code, since you're using DateTime, and the format expected by the WCF serializer for dates in JSON is something like \/Date(1234567890)\/. You can change your class to support the format you have by following the logic described at MSDN Link (fine-grained control of serialization format for primitives), and shown in the code below.

public class StackOverflow_8086483
{
    [DataContract]
    public class Eval //Models an evaluation
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Submitter { get; set; }

        [DataMember]
        public string Comment { get; set; }

        [DataMember(Name = "TimeSubmitted")]
        private string timeSubmitted;

        public DateTime TimeSubmitted { get; set; }

        public override string ToString()
        {
            return string.Format("Eval[Id={0},Submitter={1},Comment={2},TimeSubmitted={3}]", Id, Submitter, Comment, TimeSubmitted);
        }

        [OnSerializing]
        void OnSerializing(StreamingContext context)
        {
            this.timeSubmitted = this.TimeSubmitted.ToString("MM/dd/yyyy h:mm tt", CultureInfo.InvariantCulture);
        }

        [OnDeserialized]
        void OnDeserialized(StreamingContext context)
        {
            DateTime value;
            if (DateTime.TryParseExact(this.timeSubmitted, "MM/dd/yyyy h:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
            {
                this.TimeSubmitted = value;
            }
        }
    }

    [ServiceContract]
    public interface IEvalService
    {
        [OperationContract]
        [WebInvoke(RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json, UriTemplate = "eval",
           BodyStyle = WebMessageBodyStyle.Wrapped)]
        void SubmitEval(Eval eval);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    class EvalService : IEvalService
    {
        public void SubmitEval(Eval eval)
        {
            Console.WriteLine("Received eval: {0}", eval);
        }
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        WebServiceHost host = new WebServiceHost(typeof(EvalService), new Uri(baseAddress));
        host.Open();
        Console.WriteLine("Host opened");

        string data = "{\"eval\":{\"Comment\":\"testComment222\",\"Id\":\"doesntMatter\", \"Submitter\":\"Tom\",\"TimeSubmitted\":\"11/10/2011 4:00 PM\"}}";
        WebClient c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "application/json; charset=utf-8";
        c.UploadData(baseAddress + "/eval", Encoding.UTF8.GetBytes(data));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
查看更多
家丑人穷心不美
3楼-- · 2020-02-29 10:55

For POST you need to use BodyStyle = WebMessageBodyStyle.Bare NOT
WebMessageBodyStyle.Wrapped

    [OperationContract]
    [WebInvoke(RequestFormat = WebMessageFormat.Json,ResponseFormat = WebMessageFormat.Json,UriTemplate = "eval",BodyStyle = WebMessageBodyStyle.Bare)]
    void SubmitEval(Eval eval);
查看更多
登录 后发表回答