1

So, I have been messing around with webservices for a while now, and I keep getting back to some basics, that I never seem to get right.

Question 1:

When using a WebServiceHost in .NET/C#, you can define a method/endpoint as using GET/POST/etc. Setting up a GET-method is easy and it works pretty much directly, and its easy to understand how it works. For example:

[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/PutMessage/{jsonString}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
string PutMessage(string jsonString);

If I call http:///MyWebService/PutMessage/{MyJsonString} I get passed on the the method, and all is well (more or less).

But then what does it mean when I define this as a POST instead?

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/PutMessage/{jsonString}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
string PutMessage(string jsonString);

What does the UriTemplate do here? If I do a POST, I expect the data to be contained not in the URI, but in the "data section" of the post. But do I define the variable name in the data section? How does the WebServiceHost/.NET know that what is contained in the "data section" of the post is to be put into the variable jsonString? How do I post the data from the client side (not C#, let's say JQuery instead) so that it is interpreted correctly on the serer side?

(And how does the WebMessageFormat affet things? I have read everywhere about this (MSDN, Stackoverflow etc) but haven't found a clear and good answer.)

Question 2:

In my attempts to understand this, I thought I'd make a very simple POST-method, like this:

[OperationContract]
[WebInvoke]
string PutJSONRequest(string pc);

I then try call this method using Fiddler, but that does not work at all. I just get a 400 error back, saying "HTTP/1.1 400 Bad Request". I have a breakpoint on the very first line in the code of the method, and the method itself contains nothing:

public string PutJSONRequest(string pc)
{
    return null;
}

Again, how does .NET know that what I POSTed using Fiddler should be contained in the "string pc"? How does it interpret it as a string, and what type of string (UT8, ASCII etc)?

This is the RAW HTTP request, sent from Fiddler:

POST http://<myip>:8093/AlfaCustomerApp/PutJSONRequest HTTP/1.1
User-Agent: Fiddler
Host: <myip>:8093
Content-Length: 3
Content-type: application/x-www-form-urlencoded; charset=UTF-8

asd

and it doesnt matter what type of Content-type I use, as far as I can see.

The response is a standard-thing, that I am not in control of myself:

HTTP/1.1 400 Bad Request
Content-Length: 1165
Content-Type: text/html
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 15 Oct 2012 15:45:02 GMT

[then HTML code]

Any help would be appreciated. Thanks.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Ted
  • 19,727
  • 35
  • 96
  • 154

2 Answers2

5

I think a simple code can answer all your questions

Task.Factory.StartNew(()=>StartServer());
Thread.Yield();
StartClient();

void StartServer()
{
    Uri uri = new Uri("http://localhost:8080/test");
    WebServiceHost host = new WebServiceHost(typeof(WCFTestServer), uri);
    host.Open();
}

void StartClient()
{
    try
    {
        WebClient wc = new WebClient();

        //GET
        string response1 = wc.DownloadString("http://localhost:8080/test/PutMessageGET/abcdef");
        //returns: "fedcba"

        //POST with UriTemplate
        string response2 = wc.UploadString("http://localhost:8080/test/PutMessagePOSTUriTemplate/abcdef",
                                            JsonConvert.SerializeObject(new { str = "12345" }));
        //returns: fedcba NOT 54321


        //POST with BodyStyle=WebMessageBodyStyle.WrappedRequest
        //Request: {"str":"12345"}
        wc.Headers["Content-Type"] = "application/json";
        string response3 = wc.UploadString("http://localhost:8080/test/PutMessagePOSTWrappedRequest",
                                            JsonConvert.SerializeObject(new { str="12345" }));

        //POST with BodyStyle=WebMessageBodyStyle.Bare
        wc.Headers["Content-Type"] = "application/json";
        string response4 = wc.UploadString("http://localhost:8080/test/PutMessagePOSTBare", "12345" );

    }
    catch (WebException wex)
    {
        Console.WriteLine(wex.Message);
    }
}

[ServiceContract]
public class WCFTestServer
{
    [OperationContract]
    [WebInvoke(Method = "GET", UriTemplate = "/PutMessageGET/{str}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
    public string PutMessageGET(string str)
    {
        return String.Join("", str.Reverse());
    }

    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "/PutMessagePOSTUriTemplate/{str}", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
    public string PutMessagePOSTUriTemplate(string str)
    {
        return String.Join("", str.Reverse());
    }

    [OperationContract]
    [WebInvoke(Method = "POST", BodyStyle=WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
    public string PutMessagePOSTWrappedRequest(string str)
    {
        return String.Join("", str.Reverse());
    }

    [OperationContract]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
    public string PutMessagePOSTBare(string str)
    {
        return String.Join("", str.Reverse());
    }
}

PS: You can find the JsonConvert here

L.B
  • 114,136
  • 19
  • 178
  • 224
  • Thanks... So, in PutMessagePOSTUriTemplate - where does the JSON-part go? – Ted Oct 17 '12 at 13:09
  • Would you mind posting the replies/answers to PutMessagePOSTWrappedRequest and PutMessagePOSTBare too? – Ted Oct 17 '12 at 13:10
  • Lastly, if I try a simple POST method, as described in my question, the method on the server-side is never called. – Ted Oct 17 '12 at 13:15
  • **a)** `PutMessagePOSTUriTemplate`: Server Ignores the json and uses the parameter in url **b)** Replies of : `PutMessagePOSTWrappedRequest and PutMessagePOSTBare` : "54321" **c)** I don't know how you use fiddler to POST messages but it can be related to `Content-Type` (application/json) – L.B Oct 17 '12 at 16:39
  • Fiddler has a "composer" function, where you can "compose" a POST request manually. Thats the one I am using. So, in **a)** the data of the POST is ignored? How is the parameters sent, query string somehow? – Ted Oct 18 '12 at 11:54
  • Did you see the url `http://localhost:8080/test/PutMessagePOSTUriTemplate/**abcdef**` – L.B Oct 18 '12 at 11:57
  • yes, I understand how to build the request. But I also am a bit puzzled what it actually means to do a POST, but then sending the data in teh query string. Doesnt that take away the whole point of doing a POST? Perhaps the different between GET and POSt is nothing more than the words "POST" and "GET", but it has really no other meaning? – Ted Oct 18 '12 at 12:28
  • @Ted I think this is a implementation decision. If you say "I will pass the parameter in the url" but send it both in url and in message, server can pick whichever it wants... – L.B Oct 18 '12 at 12:33
  • Sorry for very late reply. I think I sort of understand it. If I pass data in the "data" area of a POST, then its "raw" format, so I need my own protocol there to interpret what is being sent. If I use the query string, then I get the different variables, like &var1=somevalue. I just need to figure out how to get the data in the POST so that it works... – Ted Nov 02 '12 at 10:59
  • 1
    I still havent figured out how to access the "data" in a POST. The examples above assumes that there is a variable called "str", but where is that variable defined? Where does it come from? How does .NET know to place the "data" in the "string str"? – Ted Nov 13 '12 at 18:22
3

I found out the answer. This is how to define a method, that can take the raw data sent in a HTTP POST:

[OperationContract]
[WebInvoke(BodyStyle=WebMessageBodyStyle.Bare)]
Stream PutMessage(Stream data);

and the implementation is like this:

public Stream PutMessage(Stream data)
{
    byte[] buffer = new byte[65535];

    int bytesRead, totalBytes = 0;
    do
    {
        bytesRead = data.Read(buffer, 0, 65535);
        totalBytes += bytesRead;
    }
    while (bytesRead > 0);

    // Then you could interpret it as a String for example:
    string jsonString = Encoding.UTF8.GetString(buffer, 0, totalBytes);
    // yada yada
}
Ted
  • 19,727
  • 35
  • 96
  • 154