leepfrog
leepfrog

Reputation: 381

Create/Consume REST WebService using WCF and Universal Apps

I want to create an IIS-hosted webservice which I will consume using a universal windows store aoo (windows phone/windows 8.1/windows RT).

As I understand universal applications do not support proxy class generation and SOAP calls using "Add service reference" so I need to create a RESTful webservice and manually consume it in the universal application.

I've tried dozens of tutorials and approaches throughout the net but I never managed to actually POST data to the webservice.

I need to send objects of a custom class which is defined in a shared library to the webservice. I understand that I will need to serialize the Object and include it in the POST request, however no matter what I try I end up with different issues - e.g HTTP 400 Bad Request: The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml'; 'Json'.

I've seen several approaches to manually set the content type header, however the methods I found are not available in a universal application.

Can someone provide information or an example which is fitting my scenario (POST-ing via universal app)?

update 1: For further clarification: I am aware how WCF works and I was already able to complete a basic GET request like described in this post. However I was unable to extend that to also work with POST requests.

Some code I've tried:

public async static void SendStartup(CustomClass customObject)
{
    var httpClient = new HttpClient();
    var serialized = JsonConvert.SerializeObject(customObject);
    var response = await httpClient.PostAsync("http://localhost:49452/Metrics.svc/LogStartup", new StringContent(serialized));
    string content = await response.Content.ReadAsStringAsync();
}

Web Service Interface:

[OperationContract]
[WebInvoke(UriTemplate = "LogStartup", Method="POST", BodyStyle=WebMessageBodyStyle.Wrapped)]
string LogStartup(CustomClass obj);

Implementation:

public void LogStartup(CustomClass obj)
{
    // nothing
}

This for example failes at runtime with the error mentioned above

Upvotes: 2

Views: 4810

Answers (3)

L.B
L.B

Reputation: 116188

There are two problem with your code.

1) You have to send the Content-Type header while your are making a request

var content = new StringContent(serialized,Encoding.UTF8,"application/json");

2) You have to use BodyStyle = WebMessageBodyStyle.Bare

WebMessageBodyStyle.Bare can work with one parameter as in your example, but if you want to post more parameters then you have to use WebMessageBodyStyle.Wrapped but then, your object you post should be modified as

var serialized = JsonConvert.SerializeObject(new { obj = customObject });

Here is a working code you can test with self-hosted WCF service

async void TestRestService()
{
    var ready = new TaskCompletionSource<object>();
    Task.Factory.StartNew(() =>
    {
        var uri = new Uri("http://0.0.0.0:49452/Metrics.svc/");
        var type = typeof(Metrics);
        WebServiceHost host = new WebServiceHost(type, uri);
        host.Open();
        ready.SetResult(null);
    },TaskCreationOptions.LongRunning);

    await ready.Task;

    var customObject = new CustomClass() { Name = "John", Id = 333 };
    var serialized = JsonConvert.SerializeObject(new { obj = customObject });

    var httpClient = new HttpClient();
    var request = new StringContent(serialized,Encoding.UTF8,"application/json");
    var response = await httpClient.PostAsync("http://localhost:49452/Metrics.svc/LogStartup", request);
    string content = await response.Content.ReadAsStringAsync();
}

[ServiceContract]
public class Metrics
{
    [OperationContract]
    [WebInvoke(Method = "POST",  BodyStyle = WebMessageBodyStyle.Wrapped)]
    public string LogStartup(CustomClass obj)
    {
        return obj.Name + "=>" + obj.Id;
    }
}

public class CustomClass
{
    public string Name { set; get; }
    public int Id { set; get; }
}

PS: If you want to return a json response then you can use ResponseFormat=WebMessageFormat.Json. You should then change the WebInvoke attribute as

[WebInvoke(Method = "POST",  BodyStyle = WebMessageBodyStyle.Wrapped,ResponseFormat=WebMessageFormat.Json)]

BTW: You can still dynamically choose the returned content type(xml or json) by setting AutomaticFormatSelectionEnabled.

Upvotes: 6

Filip Skakun
Filip Skakun

Reputation: 31724

Have you seen this article?

How to use HttpClient to post JSON data

Basically it seems like you need to add more parameters to your StringContent() constructor like this:

new StringContent(serialized, System.Text.Encoding.UTF8, "application/json");

Upvotes: 2

Greg
Greg

Reputation: 11478

One thing you need to know about Windows Communication Foundation is the ABC's.

  • A : Address
  • B : Binding
  • C : Contract

So the theory is incredibly simple, though while your coding it, it is quite odd. A simple tutorial can be found here or here. Several other tutorials can be found at Code Project for this exact approach.

Understanding Polymorphism may be helpful for understanding Windows Communication Foundation as it relies heavily on it.

[ServiceContract]
public interface IContent
{
     [OperationContract]
     void DoSomething(SomeModel model);
}

So what you're doing here is defining your service, defining your method. As I mentioned above we've explicitly declared our contract but we haven't implemented our method. Also we intend to pass SomeModel which would be our Data Contract.

We will build our model:

[DataContract]
public class SomeModel
{
     [DataMember]
     public string Name { get; set; }
}

The model can be incredibly simple like above, or incredibly complex. It will depend on usage.

Now we would like to implement our method:

public class Content : IContent
{
     public void DoSomething(SomeModel model)
     {
          // Implementation
     }
}

Now on the client, you simply consume your service. Once you understand the basics and how it serializes and deserializes you can use it for REST. Which tutorials also exist for that.

Upvotes: 1

Related Questions