8

In a larger project I am having trouble getting a WCF service method to consume a JSON parameter. So I produced a smaller test case and the behaviour is echoed. If I debug the service I can see the parameter value is null at the service call. Fiddler confirms that the JSON is being sent and JsonLint confirms it is valid.

Code below with annotations from debugging.

[ServiceContract]
public interface IWCFService
{

    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "getstring")]

    string GetString();

    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
        ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "getplayer")]
    //[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest,
    //    ResponseFormat = WebMessageFormat.Json,
    //    UriTemplate = "getplayers")]
    Player GetPlayer();

    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
        ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "getplayers")]
    List<Player> GetPlayers();

    [OperationContract]
    [WebInvoke(
        Method = "POST",
        BodyStyle = WebMessageBodyStyle.Wrapped,
        ResponseFormat = WebMessageFormat.Json,
        RequestFormat = WebMessageFormat.Json,
        UriTemplate = "totalscore")]
    string TotalScore(Player player);

}

... and its implementation

public class WCFService : IWCFService
{
    public string GetString()
    {
        return "hello from GetString";
    }

    public Player GetPlayer()
    {
        return new Player()
                {
                    Name = "Simon", 
                    Score = 1000, 
                    Club = new Club()
                            {
                                Name = "Tigers", 
                                Town = "Glenelg"
                            }
                };
    }

    public List<Player> GetPlayers()
    {
        return new List<Player>()
            {
                new Player()
                    {
                        Name = "Simon", 
                        Score = 1000 , 
                        Club=new Club()
                                {
                                    Name="Tigers", 
                                    Town = "Glenelg"
                                }
                    }, 
                new Player()
                    {
                        Name = "Fred", Score = 50,
                        Club=new Club()
                                {
                                    Name="Blues",
                                    Town="Sturt"
                                }
                    }
            };
    }

    public string TotalScore(Player player)
    {
        return player.Score.ToString();
    }
}

Calling any of the first three methods works correctly (but no parameters as you'll note). Calling the last method (TotalScore) with this client code ...

function SendPlayerForTotal() {
        var json = '{ "player":{"Name":"' + $("#Name").val() + '"'
            + ',"Score":"' + $("#Score").val() + '"'
            + ',"Club":"' + $("#Club").val() + '"}}';

        $.ajax(
        {
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: "http://localhost/wcfservice/wcfservice.svc/json/TotalScore",
            data: json,
            dataType: "json",
            success: function (data) { alert(data); },
            error: function () { alert("Not Done"); }
        });
    }

... results in ...

There was an error while trying to deserialize parameter http://tempuri.org/:player. The InnerException message was 'Expecting state 'Element'.. Encountered 'Text' with name '', namespace ''. '.

I have tried sending an unwrapped version of the JSON ...

{"Name":"Simon","Score":"100","Club":"Rigby"}

but at the service the parameter is null, and no formatter exceptions.

This is the system.serviceModel branch of the service web.config:

<system.serviceModel>
<services>
    <service name="WCFService.WCFService" behaviorConfiguration="WCFService.DefaultBehavior">
        <endpoint address="json" binding="webHttpBinding" contract="WCFService.IWCFService" behaviorConfiguration="jsonBehavior"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    </service>
</services>

<behaviors>
    <serviceBehaviors>
        <behavior name="WCFService.DefaultBehavior">
            <serviceMetadata httpGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
    </serviceBehaviors>

    <endpointBehaviors>
        <behavior name="jsonBehavior">
            <webHttp/>
        </behavior>             
    </endpointBehaviors>
</behaviors>

And here is the Player DataContract.

[DataContract(Name = "Player")]
    public class Player
    {
        private string _name;
        private int _score;
        private Club _club;

        [DataMember]
        public string Name { get { return _name; } set { _name = value; } }

        [DataMember]
        public int Score { get { return _score; } set { _score = value; } }

        [DataMember]
        public Club Club { get { return _club; } set { _club = value; } }

    }

Any help greatly appreciated and if any other info is required, please let me know.

Many thanks.

Simon Rigby
  • 1,786
  • 4
  • 17
  • 28

3 Answers3

10

You encode the input parameter player of the method TotalScore in the wrong way.

I recommend you to use JSON.stringify function from json2.js to convert any JavaScript objects to JSON.

var myPlayer = {
    Name: "Simon",
    Score: 1000,
    Club: {
        Name: "Tigers",
        Town: "Glenelg"
    }
};
$.ajax({
    type: "POST",
    url: "/wcfservice/wcfservice.svc/json/TotalScore",
    data: JSON.stringify({player:myPlayer}), // for BodyStyle equal to 
                                             // WebMessageBodyStyle.Wrapped or 
                                             // WebMessageBodyStyle.WrappedRequest
    // data: JSON.stringify(myPlayer), // for no BodyStyle attribute
                                       // or WebMessageBodyStyle.WrappedResponse
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function(data, textStatus, xhr) {
        alert(data.TotalScoreResult); // for BodyStyle = WebMessageBodyStyle.Wrapped
                                      // or WebMessageBodyStyle.WrappedResponse
        // alert(data); // for BodyStyle = WebMessageBodyStyle.WrappedRequest
                        // or for no BodyStyle attributes
    },
    error: function (xhr, textStatus, ex) {
        alert("Not Done");
    }
});

If you change the BodyStyle = WebMessageBodyStyle.Wrapped attribute of the TotalScore method to BodyStyle = WebMessageBodyStyle.WrappedRequest you can change the alert(data.TotalScoreResult) in the success handle to alert(data).

roufamatic
  • 18,187
  • 7
  • 57
  • 86
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • 1
    Thank you so much .. that was the issue. Looking at the JSON in fiddler, I see that the int value is not quoted. I was under the impression that they should be. Many Thanks. – Simon Rigby Mar 31 '11 at 10:47
  • @Simon Rigby: Yes it was one from the errors. The `Club` property you serialized also in the "Form" style which are also wrong. I modified my answer additionally to show the meaning of `BodyStyle` attribute. I hope it will also help you. – Oleg Mar 31 '11 at 10:49
  • Many thanks. How does stringify go with dates. My WCF service is tripping on them as it doesn't have the /date() wrapper around it. Is that something I'd need to address separately. I'm not sure how I'd hook into that as the object json'd in one call. I hope that makes sene. – Simon Rigby Mar 31 '11 at 14:36
  • @Simon Rigby: `DateTime` or `Date` are **not standard type** which is supported by JSON. So you should either do some additional conversion on the client side (in JavaScript) or on the server side. Do you need to convert the date posted from the server to client or post the JqvqScript `Date` to the server to be easy converted to `DateTime`? I could post you some examples or probably [this post](http://stackoverflow.com/questions/3583252/json-date-parameter-passed-to-mvc-action-is-always-null/3583579#3583579) solve you problem. – Oleg Mar 31 '11 at 17:02
  • Many thanks, I ended up using a variation on Rick Strahl's Service Proxy that uses JSON2 stringify(). Thanks for all your help. :) – Simon Rigby Apr 04 '11 at 08:03
0

I got the same problems (405 methods not allowed) using WCF POST JSON data. I found on this article below

http://blog.weareon.net/calling-wcf-rest-service-from-jquery-causes-405-method-not-allowed/

Hope this help!

nvtthang
  • 604
  • 2
  • 10
  • 27
0

You have not sepcified the Method parameter on web invoke. See: http://msdn.microsoft.com/en-us/library/bb472541(v=vs.90).aspx

Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252