Search code examples
c#jsonweb-serviceswcfhal-json

Deal with application/hal+json format in wcf


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


Solution

  • 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();
        }
    }