2

I'm trying to invoke a WCF service through HTTP POST, but the service returns a 400 error. I don't know whether this is due to the OperationContract or the way I'm doing the POST. This is what the contract looks like on the server-side:

[OperationContract, WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped)]
Stream Download(string username, int fileid);

And here's how I'm trying to invoke the service through a test console app:

HttpWebRequest webRequest = WebRequest.Create("http://localhost:8000/File/Download") as   
HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] bytes = Encoding.ASCII.GetBytes("username=test&fileid=1");
Stream os = null;
webRequest.ContentLength = bytes.Length;
os = webRequest.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
os.Close();
WebResponse webResponse = webRequest.GetResponse();

edit: I should make it clear that my goal is to test the service, and not to make it accept raw HTTP POSTs. If there's a better way I can test the service, please feel free to share.

rafale
  • 1,704
  • 6
  • 29
  • 43

2 Answers2

6

This is pretty simple process, but not readily accessible or straight-forward (as is unfortunately the case with a many aspects of WCF) Please check out this post for clarification:

service contract:

[ServiceContract]
public interface ISampleService
{
    [OperationContract]
    [WebInvoke(UriTemplate = "invoke")]
    void DoWork(Stream input);
}

HTML source:

<form method="post" action="Service.svc/invoke">
    <label for="firstName">First Name</label>: <input type="text" name="firstName" value="" />
    <br /><br />
    <label for="lastName">Last Name</label>: <input type="text" name="lastName" value="" />
    <p><input type="submit" /></p>
</form>

Code Behind:

public void DoWork(Stream input)
{
    StreamReader sr = new StreamReader(input);
    string s = sr.ReadToEnd();
    sr.Dispose();
    NameValueCollection qs = HttpUtility.ParseQueryString(s);
    string firstName = qs["firstName"];
    string lastName = qs["lastName"];
}
feathj
  • 3,019
  • 2
  • 22
  • 22
  • Getting the service to accept raw POSTs isn't my goal. I'm only trying to test the service by invoking the method from a test client, and I mistakenly thought that a raw POST would've been the easiest/simplest way of going about that. Given the OperationContract in my original post, how should I invoke it? – rafale Sep 09 '11 at 04:59
  • Unfortunately, WCF does not support form-url encoding out of the box. That is why I pointed you to the "raw" solution. You could use a "GET" instead and format your arguments in a UriTemplate – feathj Sep 09 '11 at 05:19
  • If using a single Stream input the only way to POST to a WCF service, why are there code examples out there of WebInvoke/POST OperationContracts that have multiple input parameters? See this link for an example: http://msdn.microsoft.com/en-us/library/bb628610.aspx – rafale Sep 09 '11 at 05:29
  • A stream is not the only way to POST to wcf. Most people that are using WebInvoke (post), use an xml or JSON document to supply arguments. The documentation you linked to assumes a posted xml (or json) document. I know, this is not straight-forward. StackOverflow is riddled with these kind of questions. http://stackoverflow.com/questions/4913953/post-multiple-parameters-to-wcf-service http://stackoverflow.com/questions/5281148/how-pass-multiple-body-parameters-in-wcf-rest-using-webinvoke-methodpost-or-put – feathj Sep 09 '11 at 05:49
  • @rafale I used public string Ping(Stream input) and WCF Test Client is saying the operation is not supported because it uses type System.IO.Stream. Do you have any idea why Stream is not supported and what are alternative ways to read POST from curl? so my service will get data from curl http://mysite.com/service1.svc -d message=aloha – DoodleKana Aug 29 '13 at 20:11
2

If you are able to use other content types, you could instead use json, which will work in your example.

Change

webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] bytes = Encoding.ASCII.GetBytes("username=test&fileid=1");

to

webRequest.ContentType = "application/json";
byte[] bytes = Encoding.ASCII.GetBytes("{\"username\":\"test\",\"fileid\":1");

If you must use the application/x-www-form-urlencoded content type, google wcf application/x-www-form-urlencoded for several posts and other SO questions describing workarounds.

Jeff Ogata
  • 56,645
  • 19
  • 114
  • 127
  • I was using `application/x-www-form-urlencoded` because that's what I thought I was supposed to use for all POSTs. I didn't know about `application/json`, which gave me what I wanted. Thank you. – rafale Sep 09 '11 at 06:03