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);
我们有两个 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);