Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

json - POST to WCF from Fiddler succeeds but passes null values

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>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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();
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...