WCF 到 WCF。远程服务器返回意外响应:(400) 错误请求

WCF to WCF. The remote server returned an unexpected response: (400) Bad Request

我们有两个 WCF 服务,第一个是 api 网络方法。在这样创建的界面中:

[OperationContract]
[WebInvoke(
   Method = "POST",
   RequestFormat = WebMessageFormat.Json,
   ResponseFormat = WebMessageFormat.Json,
   BodyStyle = WebMessageBodyStyle.WrappedRequest
)]
RequestParams GetAccount(RequestParams requestParams);

我想从第二个 WCF 服务调用此方法。像这样:

try
{
   var myBinding = new WebHttpBinding();
   var myEndpoint = new EndpointAddress(endpointAddress);

   using (var factory = new ChannelFactory<IAccount>(myBinding, myEndpoint))
   {
       IAccount apiService = null;

       try
       {
           factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
           apiService = factory.CreateChannel();

           result = apiService.GetAccount(requestParams);

           ((ICommunicationObject)apiService).Close();
           factory.Close();
        }
        catch (Exception ex)
        {
           Fatal(ex);
           (apiService as ICommunicationObject)?.Abort();
        }
    }
}

当我使用 Postman 检查 GetAccount 方法时,它有效并且 return 预期值。但是当我尝试从第二个服务方法调用时未调用(在调试模式下检查)并且在异常中捕获 - (400)错误请求。 我认为此错误与 returned 值的 json 格式有关。当我尝试在界面中更改为 xml 时,方法被调用(使用调试模式检查)。如何配置 WebHttpBehaviour 以供使用 json?谢谢

这其实是WebMessageBodyStyle.WrappedResponse造成的。如果将 WebMessageBodyStyle 更改为 Wrapped,WCF 将再次封装该对象。这是一个演示:

 [OperationContract]
        [WebInvoke(
        Method = "POST",
        ResponseFormat = WebMessageFormat.Json, RequestFormat =
        WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.WrappedResponse
 )]
        string GetAccount(UserData requestParams);

WrappedResponse 将封装响应。

因为你的WebMessageBodyStyle是WrappedRequest,所以你在客户端发送的对象必须封装,否则会出现400 Bad Request。

我认为最好的解决方案是将 WebMessageBodyStyle 设置为 Bare,这是一个演示:

[ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebInvoke(
        Method = "POST",
        ResponseFormat = WebMessageFormat.Json, RequestFormat =
        WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare
 )]
        string GetAccount(UserData requestParams);
    }
    [DataContract(Name = "user")]
    public class UserData
    {
        [DataMember(Name = "Name")]
        public string Name { get; set; }
        [DataMember(Name = "Password")]
        public string Password { get; set; }
        [DataMember(Name = "Email")]
        public string Email { get; set; }
    }
    public class Service1 : IService1
    {
        public string GetAccount(UserData requestParams)
        {
            return "OK";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost selfHost = new ServiceHost(typeof(Service1));
            selfHost.Open();
            Console.WriteLine("Service Open");
            Console.ReadKey();
            selfHost.Close();
        }
    }

这是Program.cs。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>

    <system.serviceModel>
        <services>

            <service name="ConsoleApp113.Service1" behaviorConfiguration="ServiceBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8012/ServiceModelSamples/service"/>
                    </baseAddresses>
                </host>

                <endpoint address=""
                          binding="webHttpBinding"
                          contract="ConsoleApp113.IService1"
                          behaviorConfiguration="ESEndPointBehavior" />
            </service>
        </services>


        <behaviors>
            <endpointBehaviors>
                <behavior name="ESEndPointBehavior">
                    <webHttp helpEnabled="true"/>
                </behavior>
            </endpointBehaviors>

            <serviceBehaviors>
                <behavior name="ServiceBehavior">
                    <serviceMetadata/>
                </behavior>
            </serviceBehaviors>

        </behaviors>

    </system.serviceModel>
</configuration>

这是web.config。

class Program
    {
        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            [WebInvoke(
            Method = "POST",
            ResponseFormat = WebMessageFormat.Json, RequestFormat =
            WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare
     )]
            string GetAccount(UserData requestParams);
        }
        [DataContract(Name = "user")]
        public class UserData
        {
            [DataMember(Name = "Name")]
            public string Name { get; set; }
            [DataMember(Name = "Password")]
            public string Password { get; set; }
            [DataMember(Name = "Email")]
            public string Email { get; set; }
        }
        static void Main(string[] args)
        {
            var myBinding = new WebHttpBinding();
            var myEndpoint = new EndpointAddress("http://localhost:8012/ServiceModelSamples/service");

            using (var factory = new ChannelFactory<IService1>(myBinding, myEndpoint))
            {
                    IService1 apiService = null;
                    factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
                    apiService = factory.CreateChannel();
                    UserData userData = new UserData();
                    userData.Email = "Test";
                    userData.Name = "Test";
                    userData.Password = "Test";
                    string result = apiService.GetAccount(userData);
                    ((ICommunicationObject)apiService).Close();
                    factory.Close();
                    Console.WriteLine(result);
                    Console.ReadLine();
              
            }
        }
    }

这是客户端代码。

更新:

很遗憾,WCF 中没有这样的设置。但是你可以自己封装一个wrapped类型的对象:

    [DataContract]
    public class GetAccount {
        [DataMember(Name = "RequestParams ")]
        public RequestParams requestParams ;
    }

可以将RequestParams封装成GetAccount,然后直接将GetAccount发送给服务端。所以为了成功调用WCF服务你必须修改服务接口:

[OperationContract]
[WebInvoke(
   Method = "POST",
   RequestFormat = WebMessageFormat.Json,
   ResponseFormat = WebMessageFormat.Json,
   BodyStyle = WebMessageBodyStyle.WrappedRequest
)]
RequestParams GetAccount(GetAccount requestParams);