WCF Webinvoke POST 给出(400)特定服务器的错误请求

Posted

技术标签:

【中文标题】WCF Webinvoke POST 给出(400)特定服务器的错误请求【英文标题】:WCF Webinvoke POST giving (400) Bad Request for specific server 【发布时间】:2020-07-04 17:13:27 【问题描述】:

早安/晚安,

我是 WCF 新手,并创建了一个示例应用程序。问题是我将 json 字符串作为请求传递,但收到 400:Bad request 错误。我的示例的详细信息如下:

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;
            
        
    

主要调用函数:

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

在运行应用程序时出现以下错误:

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

我尝试过的:我已经更改了发送和接收的超时时间,内容类型是 application/json。该请求仅针对此服务器引发错误。我有另一台服务器,我尝试过,POST 正在服务器中传递。两台服务器具有相同的配置。当我为错误的服务器运行 Fiddler 时,POST 调用成功。从 POSTMAN 向错误的服务器发送完全相同的请求会给出成功(200 OK)状态,并且在这两种情况下我都得到了正确的响应。

注意:WEBGET、WEBInvoke DELETE 在服务器上工作正常。只有 WEBInvoke POST 不适用于特定服务器。有人可以帮我解决这个问题吗?提前致谢。

【问题讨论】:

【参考方案1】:

这部分有效负载在我看来不正确。我更改了 1 行,见下文。

唯一的补充是在recipient.id 值周围添加引号

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\""
+"";

【讨论】:

我已经添加了报价并运行了程序,但到目前为止还没有运气。 :(【参考方案2】:

为什么要使用代理(通道工厂)调用 WCF Restful 服务?如果确实如此,我们应该使用服务基地址而不是POST URL。此外,服务合同应与服务器相同。

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

此代码 sn-p 应使用服务基地址通过代理发送请求。 实际上,我们通常在调用Webhttpbinding创建的WCF服务的同时通过POSTMan/fiddler发送请求。此外,我们应该使用由 URITemplate 属性修饰的 Uri。 如果问题仍然存在,请随时告诉我。

【讨论】:

uri 的格式似乎是正确的,因为在调试应用程序时,我得到了正确的绝对 uri。我认为需要对 json 请求进行反序列化。我将尝试同样的方法并发布我的发现。谢谢。 是的,400 bad request 通常与参数的格式有关,而大多数情况下,反序列化过程由 WCF 框架完成。我没有看到你在函数的签名中添加了强类型参数。 我已经反序列化了我的请求,但仍然无法找到突破口。 :(

以上是关于WCF Webinvoke POST 给出(400)特定服务器的错误请求的主要内容,如果未能解决你的问题,请参考以下文章

Wcf 中的 WebInvoke POST“方法不允许”

预检的 Wcf webHttpBinding 响应具有无效的 HTTP 状态代码 400

带有参数的 Wcf Restful 服务发布用户名

在 wcf rest 中发送 xml 请求时出现错误请求 400

如何以 JSON 形式返回 WCF 服务 POST 响应

WCF POST 方法返回“方法不允许”