4

So simple in theory, but I have never been a wiz at WCF configuration. What I am trying to do is this: I have a WCF method that matches this signature:

[OperationContract]
[WebInvoke(Method = "POST")]
Stream PostPackets(Stream rawPackets);

All I really care about is getting a byte array from an Android/iPhone/Blackberry/any other type of device, to my wcf service, process the array and then send back a different array of bytes. For all I care, it could look like:

[OperationContract]
[WebInvoke(Method = "POST")]
byte[] PostPackets(byte[] rawPackets);

Though all examples I see seem to use Stream.

I have read many different articles and posts with no straight answer on how to do this outside the context of a file transfer (which is not my intention). Here are the problems I am facing:

1- I assume I need to use webHttpBinding to make this service RESTful. Is this right? If so, can you point me to a sample configuration?

2 - (And this is absolutely what I cannot find anywhere!) I need to be sure that this is not going to be a huge pain for the device developers to consume. Can you show me examples of both Android and iPhone devices consuming a RESTful service AND (very important) how they would send a byte array to my service?

Please forgive my noobiness... WCF configuration is one of those things I don't get to do every day. Once I get my configuration figured out, I generally move on and never have to touch it until my next project (which could be a very long time). Please help!

UPDATE

My colleague suggested that we use http handlers instead of wcf. Do we really have to resort to that?
e.g.:

public void ProcessPackets (HttpContext context) 

UPDATE 2:

I am wondering, is there any way to do this without JSON? Is there any downside/alternative to posting the array as type "text/plain"?

saml
  • 463
  • 3
  • 14

2 Answers2

4

Maybe this simple (and working example) can help

SERVER

void StartServer()
{
    Task.Factory.StartNew(() =>
    {
        WebServiceHost host = new WebServiceHost(typeof(MyService), new Uri("http://0.0.0.0:80/MyService/"));
        host.Open();
    });
}

[ServiceContract]
public class MyService
{
    [OperationContract]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    public byte[] PostPackets(byte[] rawPackets)
    {
        rawPackets[0] = 99;
        return rawPackets;
    }
}

CLIENT

<html>
<script src='jquery-1.8.3.min.js'></script>

<body>
<script>
         $(document).ready(function () {
            $.ajax({
                type: "POST",
                contentType: "application/json",
                url: "/MyService/PostPackets",
                data: JSON.stringify({rawPackets:[65,66,67]}), 
                dataType: "text",
                success: function (data) {
                    //var div = $("#test").empty();
                    //$("#test").html(data.d);
                    alert('success');
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert('error');
                }
            });
        });
</script>
</body>
</html>
I4V
  • 34,891
  • 6
  • 67
  • 79
  • Thanks this is good! But I need to be sure it's doable from Android/blackberry/iphone as well. – saml Jan 21 '13 at 15:49
  • also, in your example above, can you also post what the web.config would look like for your wcf configuration? Thanks – saml Jan 21 '13 at 15:55
  • Like I said, please forgive that I am for all intents and purposes, a wcf RESTful service noob, but if you look here: http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide don't I have to set up my web.config file like step 6? – saml Jan 21 '13 at 16:10
  • 1
    @saml To run above code, you can create **any** type of application (ie. winforms, Console). Just call `StartServer` somewhere within your code and it is done. I can not think a simpler way to create a RESTful service. – I4V Jan 21 '13 at 16:14
  • Wow if this works in my current environment (the wcf service is currently hosted as a windows service) then you just saved me lots of time fighting with the config file to support multiple binding types. Btw, does this method support ssl? – saml Jan 21 '13 at 16:17
  • Also, I'm assuming that "http://0.0.0.0:80/MyService/" should be replaced by: "http://mydomainname/MyService/" ? – saml Jan 21 '13 at 16:18
  • @saml for ssl just replace http with https (assuming you have already managed the works related with certificate etc.) – I4V Jan 21 '13 at 16:20
  • @saml `0.0.0.0` is used to allow accepting connections from anywhere. If you write it as mydomain/MyService only computers from mydomain can access to this service. – I4V Jan 21 '13 at 16:21
  • Seems too good to be true, I can't wait to try it. I don't suppose you would happen to know how to consume this service from an android device app would you (not a web app)? – saml Jan 21 '13 at 16:25
  • @saml Sorry, no. But I remember a post. Let me search. – I4V Jan 21 '13 at 16:30
  • @saml maybe this code in question can help http://stackoverflow.com/questions/13165533/deserialize-json-object-sent-from-android-app-to-wcf-webservice – I4V Jan 21 '13 at 16:33
  • Perfect I'll try it out and let you know soon! – saml Jan 21 '13 at 17:00
  • Unfortunately, this did not work. I get the error "Service cannot be started. System.InvalidOperationException: Service 'MyService' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element." – saml Jan 27 '13 at 02:33
1

In the end the solution was as follows. (It was quite simple indeed)

Service side:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    [WebInvoke(Method = "POST",
        BodyStyle = WebMessageBodyStyle.Wrapped,
        UriTemplate="TestMethod/")]
    Stream TestMethod(Stream input);

}

public class MyService: IMyService
{
    Stream IMyService.TestMethod(Stream input)
    {
        byte[] buffer = new byte[10000];
        int bytesRead, totalBytesRead = 0;
        this.currentResponseOffset = 0;
        do
        {
            bytesRead = input.Read(buffer, 0, buffer.Length);
            totalBytesRead += bytesRead;
        } while (bytesRead > 0);

        input.Close();

        return new MemoryStream(buffer, 0, totalBytesRead);
    }
}

With a configuration that looks like:

<services>
  <service name="MyService" >
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8732/Design_Time_Addresses/MyService/" />
        <add baseAddress="net.tcp://localhost:4504/Design_Time_Addresses/MyService/" />
      </baseAddresses>
    </host>
    <endpoint address="MyTCPService" binding="netTcpBinding" contract="IMyTCPService">
    </endpoint>

    <endpoint address="MyHTTPService" binding="webHttpBinding" behaviorConfiguration="web" contract="IMyService"></endpoint>  

    <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
    <endpoint address="mex"  binding="mexHttpBinding" contract="IMetadataExchange"/>
    <!--<endpoint binding="mexHttpsBinding" bindingConfiguration="" contract="IMetadataExchange" />-->
  </service>
</services>
<behaviors>
   <endpointBehaviors>
    <behavior name="web">
      <webHttp />
    </behavior>
  </endpointBehaviors>
</behaviors>

I was able to test it out with a .net client, though the whole point of this post was to know how to make it cross platform. I guess we will see soon enough! Thanks @I4V for your help, it was much appreciated.

saml
  • 463
  • 3
  • 14