locked
Post Json to Rest which listen inside a Windows Service RRS feed

  • Question

  • I have a WCF listener on Windows Service.

    when i send a Json to this listener

    it appears as xml for some reason...

    a the detention in the interface :

     [OperationContract]
            [WebInvoke(Method = "POST",
                BodyStyle = WebMessageBodyStyle.Bare,
                RequestFormat = WebMessageFormat.Json,
                UriTemplate = "ReceiveData")]
            void ReceiveData();

    the Json sent with header : "application/json; charset=utf-8"

    not clear to me the reason for this.

    any idea?

    Monday, July 27, 2020 11:53 AM

All replies

  • Using WCF and JSON is an odd combination. In general if you want JSON create a real REST API. It is faster and simpler. WCF is historically XML based because it is based upon SOAP. While WCF REST is supported it is, in my opinion, a shim to get existing WCF services that cannot be rewritten into the JSON world.

    It is unclear to me whether the issue is with the client sending JSON and the server not recognizing it or the server returning XML when the client expected JSON. The client and server must agree on the configuration (bindings) to get this to work. 

    Taking a quick look at example WCF REST code I see the following differences.

    1. You are using Bare for the body instead of the standard Wrapped. That means the JSON isn't going to be wrapped which may require additional configuration to get it to work.

    2. The binding on the client and server need to be using webHttpBinding so the correct HTTP binding is used instead of the traditional SOAP or binary bindings. The client and server must be configured the same.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, July 27, 2020 5:55 PM
  • Hi, i will explain.

    I have a client that is sending a JSON, i toke a Wireshark : ( i sent it to a web page just to show it  in a wireshark

    POST /Log.ashx HTTP/1.1
    Content-Type: application/json; charset=utf-8
    Content-Length: 510
    Expect: 100-continue
    Connection: Keep-Alive
    
    HTTP/1.1 100 Continue
    {
    	"id": 8029,
    	"send": "20200727a",
    	"createdDate": "2020-07-27T21:14:15.743",
    	"status": 3,
    	"error": 58,
    }
    {"id":8029,"sendId":"20200727a","createdDate":"2020-07-27T21:14:15.743","fromNumber":"97250121214g","toNumber":"972525447467","credits":1,"status":3,"reference":"peleg","text":"peleg<#> OpenGate verification code: 3925 ...   KiF2W5oCRZj <#> j    yyyy ! @ # $ % ^ & * ( ) _ - + =    gggg ? / . < > | { } [ ]  ##SmsUnsubscribeURL## .......... df glksdfg l;kdsfl;g l;dskf gl;sdkf gl;ksdf g;kdsf g;dkf g;kdsf g     ##SmsUnsubscribeURL##  ##SmsUnsubscribeURL##","errorTypeID":0,"Schedule":"2020-07-27T21:14:15.743"}HTTP/1.1 200 OK
    HTTP/1.1 200 OK

    from the other side i have a listener in side a Windows Service that listen and get the request 

    it is setup as follows :

    ServiceHost = new ServiceHost(typeof(Service1), new System.Uri(baseAddress));
    ServiceHost.AddServiceEndpoint(typeof(WindowsServiceTemplate.IService1),
                        new WebHttpBinding(), baseAddress).Behaviors.Add(new WebHttpBehavior());
                    ;
                    ServiceMetadataBehavior smb = ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
                    if (smb == null)
                        smb = new ServiceMetadataBehavior();
                    smb.HttpGetEnabled = true;
                    smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                    ServiceHost.Description.Behaviors.Add(smb);
                    ServiceHost.Open();

    and the mothed which get the event as described in the Post begging.

    Thanks!

    Monday, July 27, 2020 6:22 PM
  • OK so the client is sending JSON to the server. So what is the issue then? Are you just asking why it is being sent with the given content type? If so that is because it is sending JSON to the server and the server needs the content type to know how to parse the body of the request.

    If you're getting an error on the server side about it not understanding the request then it is an issue with the server configuration. The code you posted seems to be setting up an endpoint with a binding but it doesn't appear you really configure it to support JSON. Personally I'd recommend you configure the WCF using the config file and then load the configuration directly from the config rather than hard coding it into the code like you're doing now. It'll make it easier to change it later without recompiling. 

    If you are getting errors then please post the exception information.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, July 27, 2020 7:02 PM
  • Hi

    my problem is on the server side, and not clear to me how to set it to receive a JSON.

    since now i get it translated into xml.

    Thanks

    Monday, July 27, 2020 7:05 PM
  • So it is coming from the client as JSON (because that is how you configured it) but on the server side you are getting a server error because it is expecting XML? That means you did not configure the server side to accept JSON. 

    In your original post you listed a method that was configured with WebInvoke attribute. Given your later post that means that `WindowsServiceTemplate.IService1` is where this operation was defined correct? That is necessary because the interface contract determines the supported type in WCF. Assuming your `Service1` class implements this interface then the runtime will properly handle converting to/from JSON for you. You don't need to do anything. Please post the relevant code for both the interface and service class.

    Have you tried loading up your WCF service in the WCF test client and sent a test message to it. This will completely eliminate any client side issues and confirm the issue is on your server? Also verify your endpoint you are trying to use on the server lines up with what the client is using. The WCF test client will help identify this as well.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, July 27, 2020 7:46 PM
  • Hi

    this is the interface Service1:

     [ServiceContract()]
        public interface IService1
        {
    [WebInvoke(Method = "POST",
                BodyStyle = WebMessageBodyStyle.WrappedRequest,
                RequestFormat = WebMessageFormat.Json,
                UriTemplate = "ReceiveData")]
            [OperationContract]
            void ReceiveData();
    }

    implementation of the Service1:

    public void ReceiveData()
            {
                string xml = string.Empty;
                try
                {
                    xml = OperationContext.Current.RequestContext.RequestMessage.ToString();
                   
                }
                catch (Exception e)
                {
                    logger.Error($"ReceiveData,failed on xml parse,xml={xml},Err={e.Message}");
                }
    
    
            }

    Monday, July 27, 2020 8:20 PM
  • Ok I'm starting to think this isn't an XML vs JSON issue but rather a misunderstanding of what WCF is bringing to the table. Your WCF code won't ever be working with XML/JSON directly. That completely defeats the purpose of using it (and REST APIs for that matter). In your very specific example you have no parameters nor return any data so to call that API a client would simply POST an empty body to the endpoint. They would expect to get a 200 response with no data. That is exactly what your service (and method) is saying. No input and no output. 

    More importantly you won't be using `OperationContext` for anything as this would be an advanced use case if you needed it. To pass data to a WCF method you specify the parameter(s) in the WCF method you expect. If you want the response to contain data you return a value from the method. The WCF infrastructure will handle converting the underlying request/response to/from JSON or XML. You won't do any of that by hand. This is how WCF works (irrelevant of content type). 

    Let's look at an example. Note that if you create a brand new WCF service app in Visual Studio you'll basically see the exact same syntax, minus JSON but we'll create a custom one to line up with your example.

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebInvoke(Method = "POST", 
                    BodyStyle = WebMessageBodyStyle.Bare, 
                    RequestFormat = WebMessageFormat.Json, 
                    ResponseFormat = WebMessageFormat.Json,
                    UriTemplate = "/ReceiveData")]
        ResponseData ReceiveData ( RequestData data );
    
        [OperationContract]
        [WebInvoke(Method = "GET",
                    BodyStyle = WebMessageBodyStyle.Bare,
                    RequestFormat = WebMessageFormat.Json,
                    ResponseFormat = WebMessageFormat.Json,
                    UriTemplate = "/ping")]
        string Ping ();
    }
        
    [DataContract]
    public class RequestData
    {        
        [DataMember]
        public int Id { get; set; }
    
        [DataMember]
        public string SendId { get; set; }
    
        [DataMember]
        public DateTime CreatedDate { get; set; }
    
        [DataMember]
        public string FromNumber { get; set; }
    
        [DataMember]
        public string ToNumber { get; set; }
    
        //Rest of members go here with DataMember attribute and appropriate public property
    }
    
    //Example response object marked as DataContract
    [DataContract]
    public class ResponseData
    {
        //Add response data here marked with DataMember
        [DataMember]
        public string Result { get; set; }
    }
    
    public class Service1 : IService1
    {
        public string Ping () => "Success";
    
        public ResponseData ReceiveData ( RequestData request )
        {
            //Data is already parsed out so use normally - making stuff up here
            if (request.Id > 0)
                return new ResponseData() { Result = "OK" };            
            else
                return new ResponseData() { Result = "Missing ID" };
        }
    }
    
    Now the `request` should contain the data you are passing from the REST call. The runtime will handle wrapping/unwrapping it. Note that I'm not 100% sure about whether you should be using Wrapped or Bare but I think Wrapped is the correct option.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, July 27, 2020 9:50 PM
  • Thanks alot!

    i was trying to read the data from the contect while i can get it lread as an object in the method params!

    Monday, July 27, 2020 10:42 PM
  • Hi want 2 Learn,
    Based on the description, it is mostly related to WCF. So it is recommended to ask the questions in WCF forum and you can get more professional answer.
    Thank you for your understanding.
    Best Regards,
    Daniel Zhang


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, July 28, 2020 1:48 AM
  • Hi first thanks for your help.

    second i have an issue with the DateTime (i went field by field and found there is an issue

    i receive DateTime as follows :

    "createdDate""2020-07-26T13:12:31.133",

    the createdDate is defined as :

     [DataMember]
            public DateTime createdDate { get; set; }

    when i send this to the end point i get an error that the server failed to process it

    Tuesday, July 28, 2020 7:57 AM
  • Dates can be tricky because of formatting. If you're having an issue parsing a date as is then use a string for the property instead. On the server side you can always create a converter property that is not marked as a data member and converts to DateTime.

    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, July 28, 2020 1:19 PM
  • Hi

    converter property?

    how to do it?

    Tuesday, July 28, 2020 8:35 PM
  • public class MyData
    {
       [DataMember]
       public string CreateDate { get; set; }
    
       public DateTime? CreationDate
          => DateTime.TryParse(CreateDate, out var dt) ? dt : null;
    }
    Something like this. Probably need to modify to fit your needs (or even compile). Basically expose a public property containing the formatted data but don't expose it to WCF, use the string for that.

    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, July 28, 2020 8:46 PM
  • thanks for all of your support
    Wednesday, July 29, 2020 6:16 AM