WCF Webinvoke POST giving (400) Bad Request for specific server

463 views Asked by At

Good morning/evening,

I am new to WCF and have created a sample application. The problem is I am passing a json string as a request but getting 400:Bad request error. The details of my sample is given below:

ISampleService.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace SampleWCF
{
    [ServiceContract]
    public interface ISampleService
    {
        [OperationContract]
        [WebInvoke(UriTemplate = "/folder_entries/{mFileID_param}/shares?notify=true", Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string AddShareToFileNotify(string mFileID_param, string rqst_param);
    }
}


#region TestSample
[DataContract]
public class TestSample
{
    public TestSample() { }

    [DataMember(Name = "recipient")]
    public Recipient Recipient { get; set; }

    [DataMember(Name = "role")]
    public String Role { get; set; }

    [DataMember(Name = "access")]
    public TestAccess access{ get; set; }

    [DataMember(Name = "can_share")]
    public bool CanShare { get; set; }

    [DataMember(Name = "days_to_expire")]
    public int DaysToExpire { get; set; }

}

    #region TestAccess 
    [DataContract]
    public class TestAccess 
    {
        #region Attributes

        [DataMember(Name = "role")]
        public String Role { get; set; }

        [DataMember(Name = "rights")]
        public AccessRights AccessRights { get; set; }

        #endregion

        #region Constructor
        public TestAccess () { }
        #endregion
    }
    #endregion

    #region rights
    [DataContract]
    public class AccessRights
    {
        public AccessRights() { }

        [DataMember(Name = "testinternal")]
        public Boolean Internal { get; set; }

        [DataMember(Name = "testexternal")]
        public Boolean External { get; set; }

        [DataMember(Name = "public")]
        public Boolean Public { get; set; }

        [DataMember(Name = "max_role")]
        public String Max_Role { get; set; }

        [DataMember(Name = "grant")]
        public Boolean Grant { get; set; }

    }
    #endregion

    #region Recipient
    [DataContract]
    public class Recipient
    {
        public Recipient() { }

        [DataMember(Name = "id")]
        public string ID { get; set; }

        [DataMember(Name = "type")]
        public string Type { get; set; }

    }
    #endregion
#endregion

SampleService.svc.cs

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using System.ServiceModel.Security;
using System.Net;
using System.IO;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

namespace SampleWCF
{
    public class SampleService : ISampleService
    {
        private ISampleService client = null;
        private WebChannelFactory<ISampleService> cf = null;
        private Uri uri = null;
        private WebHttpSecurityMode mode = WebHttpSecurityMode.Transport;
        public const string CERTIFICATE_TRUST_STORE_NAME = "Trust";

        //Method to Validate if the server certificate is valid or not
        private static bool ValidateServerCertificate(object sender,
                                                      X509Certificate certificate,
                                                      X509Chain chain,
                                                      SslPolicyErrors sslPolicyErrors)
        {
            bool result = false;
            X509Store store = null;

            try
            {
                // If the certificate is valid signed certificate, return true.
                if (SslPolicyErrors.None == sslPolicyErrors)
                {
                    return true;
                }

                // If there are errors in the certificate chain, look in the certificate store to check
                // if the user has already trusted the certificate or not.
                if ((0 != (sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors)) ||
                    (0 != (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch)))
                {
                    store = new X509Store(CERTIFICATE_TRUST_STORE_NAME, StoreLocation.CurrentUser);
                    store.Open(OpenFlags.ReadOnly);
                    result = store.Certificates.Contains(certificate);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not validate certificate!");
                result = false;
            }
            finally
            {
                if (store != null)
                    store.Close();
            }

            return result;
        }


        public ISampleService initClient(string servername,
                                         string protocol,
                                         string username,
                                         string password)
        {
            uri = new Uri(protocol + "://" + servername + ":" + @"/rest");
            WebHttpBinding binding = new WebHttpBinding();
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReceiveTimeout = TimeSpan.FromMinutes(10.0);
            binding.SendTimeout = TimeSpan.FromMinutes(10.0);
            System.Net.ServicePointManager.DefaultConnectionLimit = 200;

            binding.Security.Mode = mode;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

            cf = new WebChannelFactory<ISampleService>(binding, uri);
            cf.Credentials.UserName.UserName = username;
            cf.Credentials.UserName.Password = password;

            client = cf.CreateChannel();

            System.Net.ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

            Thread.Sleep(500);
            return client;
        }

        public string AddShareToFileNotify(string mFileID_param, string rqst_param)
        {
            using (new OperationContextScope((IContextChannel)client))
            {
                string rsp = null;
                try
                {
                    rsp = client.AddShareToFileNotify(mFileID_param, rqst_param);
                }
                catch (Exception ce)
                {
                    Console.WriteLine("Exception found!{0}",ce);
                    return rsp;
                }
                return rsp;
            }
        }
    }
}

Main Calling function:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TriggerMain
{
    class Program
    {
        static void Main(string[] args)
        {

            string mFileID = "xxxxxx";
            string rqst = "{"
    +"\"access\":{"
        +"\"role\":\"VIEWER\","
        +"\"sharing\":{"
            +"\"external\":false,"
            +"\"grant\":false,"
            +"\"internal\":false,"
            +"\"max_role\":null,"
            +"\"public\":false"

        +"}"
    +"},"
    +"\"can_share\": false,"
    +"\"days_to_expire\": 30,"
    +"\"recipient\": {"
        +"\"id\": <yyyyyy>,"
        +"\"type\": \"user\""
            +"},"
    +"\"role\": \"VIEWER\""
+"}";
            string rsp = null;

            SampleWCF.SampleService sample = new SampleWCF.SampleService();
            sample.initClient("<URL1.xxx.com>", "https", "<Username>", "<Password>");
            rsp = sample.AddShareToFileNotify(mFileID, rqst);
            Console.ReadLine();
        }
    }
}

While running the application I am getting the following error:

Exception found!System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (400) Bad Request. ---> System.Net.WebException: The remote server returned an error: (400) Bad Request.
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace:
   at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory`1 factory, WebException responseException, ChannelBinding channelBinding)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at SampleWCF.ISampleService.AddShareToFileNotify(String mFileID_param, String rqst_param)
   at SampleWCF.SampleService.AddShareToFileNotify(String mFileID_param, String rqst_param) in c:\Users\SBasu\Documents\Visual Studio 2013\Projects\SampleWCF\SampleWCF\SampleService.svc.cs:line 103

What I have tried : I have changed the timeout for send and receive, the content type is application/json. The request is throwing error for only this server. I have another server in which I have tried and the POST is passing in the server. Both the servers have the same configuration. When I run Fiddler for the erroneous server the POST call succeeds. Sending the exact same request from POSTMAN to the erroneous server gives success (200 OK) status and I am getting proper response in both of these cases.

Note: WEBGET, WEBInvoke DELETE are working fine for the server. Only WEBInvoke POST is not working for the specific server. Can anybody help me regarding this? Thanks in advance.

2

There are 2 answers

3
Abraham Qian On

Why do you call the WCF Restful service by using a proxy (channel factory)? If indeed, we should use the service base address instead of the POST URL. In addition, the service contract should be the same as the server.

uri = new Uri(protocol + "://" + servername + ":" + @"/rest") // where is the service port number? also, is the format right?

This code snippet should use a service base address to send a request by a proxy.
In fact, we usually send a request by POSTMan/fiddler while calling the WCF service created by Webhttpbinding. Moreover, we should use the Uri decorated by URITemplate attribute.
Feel free to let me know if the problem still exists.

1
Glenn Ferrie On

This part of the payload doesn't look right to me. I changed 1 line, see below.

The only addition was to add quotation marks around the recipient.id value

string rqst = "{"
+"\"access\":{"
    +"\"role\":\"VIEWER\","
    +"\"sharing\":{"
        +"\"external\":false,"
        +"\"grant\":false,"
        +"\"internal\":false,"
        +"\"max_role\":null,"
        +"\"public\":false"

    +"}"
+"},"
+"\"can_share\": false,"
+"\"days_to_expire\": 30,"
+"\"recipient\": {"
    +"\"id\": \"<yyyyyy>\"," // this line I think was wrong. added quotes around the value
    +"\"type\": \"user\""
        +"},"
+"\"role\": \"VIEWER\""
+"}";