0

I set up a configured URL endpoint (using wcf and POST method) to be trigged when something happens at my client side. it works well when the content type, for the request, is set to application/json, but it doesn't when it is set to application/json+hal, which my client wants to use. My question is how to deal with this matter and what I have to change. Here is the definition of my method in the interface:

[WebInvoke(Method = "POST", UriTemplate = "/payload", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
string payload(requestpayload jsondata);

I updated my web.config to take @carlosfigueira suggestion into consideration:

<services>
      <service behaviorConfiguration="RestServiceBehavior" name="RestRaw.RestService">
        <endpoint address="http://mywebsite.com/RestService.svc" binding="customBinding" bindingConfiguration="RawReceiveCapable" contract="RestRaw.IRestService" behaviorConfiguration="Web">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="RestServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="Web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <binding name="RawReceiveCapable">
          <webMessageEncoding webContentTypeMapperType="RestRaw.JsonHalMapper, RestRaw" />
          <httpTransport manualAddressing="true"  />
        </binding>

      </customBinding>

However, I am getting this:

500 System.ServiceModel.ServiceActivationException

any thought please

RAM
  • 2,257
  • 2
  • 19
  • 41
R.You
  • 565
  • 3
  • 15

1 Answers1

0

You can use a WebContentTypeMapper to indicate to WCF that you want to treat application/hal+json the same way as "regular" JSON (i.e., application/json). The code below shows an example of using the mapper.

public class StackOverflow_37597194
{
    [ServiceContract]
    public interface ITest
    {
        [WebInvoke(Method = "POST",
            UriTemplate = "/payload",
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare)]
        string payload(RequestPayload jsondata);
    }
    [DataContract]
    public class RequestPayload
    {
        [DataMember(Name = "_links")]
        public RequestPayloadLinks Links { get; set; }
        [DataMember(Name = "currency")]
        public string Currency { get; set; }
        [DataMember(Name = "status")]
        public string Status { get; set; }
        [DataMember(Name = "total")]
        public double Total { get; set; }
    }
    [DataContract] public class LinkObject
    {
        [DataMember(Name = "href")]
        public string Href { get; set; }
    }
    [DataContract]
    public class RequestPayloadLinks
    {
        [DataMember(Name = "self")]
        public LinkObject Self { get; set; }
        [DataMember(Name = "warehouse")]
        public LinkObject Warehouse { get; set; }
        [DataMember(Name = "invoice")]
        public LinkObject Invoice { get; set; }
    }
    public class Service : ITest
    {
        public string payload(RequestPayload jsondata)
        {
            return string.Format("{0} - {1} {2}", jsondata.Status, jsondata.Total, jsondata.Currency);
        }
    }
    public class JsonHalMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {
            if (contentType.StartsWith("application/hal+json"))
            {
                return WebContentFormat.Json;
            }
            else
            {
                return WebContentFormat.Default;
            }
        }
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        var endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding { ContentTypeMapper = new JsonHalMapper() }, "");
        endpoint.EndpointBehaviors.Add(new WebHttpBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        var requestData = @"   {
            '_links': {
                'self': { 'href': '/orders/523' },
                'warehouse': { 'href': '/warehouse/56' },
                'invoice': { 'href': '/invoices/873' }
            },
            'currency': 'USD',
            'status': 'shipped',
            'total': 10.20
        }".Replace('\'', '\"');
        Console.WriteLine(requestData);
        c.Headers[HttpRequestHeader.ContentType] = "application/hal+json";
        try
        {
            var response = c.UploadString(baseAddress + "/payload", "POST", requestData);
            Console.WriteLine(response);
        }
        catch (WebException ex)
        {
            Console.WriteLine("Exception: {0}", ex);
        }

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
carlosfigueira
  • 85,035
  • 14
  • 131
  • 171
  • Thank you @carlosfigueira, I tried your suggestion. However, I am getting this: **500 System.ServiceModel.ServiceActivationException** – R.You Jun 06 '16 at 11:39
  • What is the body of the activation exception response? Does it have more details on the exception? Or you can also enable tracing, and you should see why it failed to open the service. – carlosfigueira Jun 06 '16 at 12:50
  • I fixed my issue. In general, I used **WebContentTypeMapper** as @carlosfigueira said. then I changed my web.config as it's mentioned above but with this update: **** – R.You Jun 06 '16 at 16:53