如何在 WCF Rest 服务中创建 JSON 到 match/serialize 到 DataContract
How to create JSON to match/serialize to DataContract in WCF Rest Service
接口:
namespace SQRT_WCF
{
[DataContract]
public class PlaceOrder
{
[DataMember]
public string claimID { get; set; }
[DataMember]
public string rederenceDate { get; set; }
}
}
调用的 C# 方法:
public SQ_jsonModel.Response placeOrder(PlaceOrder argPlaceOrderJSON)
{
...
}
我已将我的网络服务附加到工作进程,并通过代码进行跟踪。到目前为止,我所尝试的一切,变量 argPlaceOrderJSON 都是空的。
我至少尝试了三种变体:
变体 #1
{
"claimID" : "value",
"rederenceDate" : "value"
}
变化 #2:
{
"PlaceOrder" : {
"claimID" : "value",
"rederenceDate" : "value"
}
变化 #3:
{
"ns0:PlaceOrder" : {
"@xmlns:ns0" : "SQRT_WCF",
"claimID" : "value",
"rederenceDate" : "value"
}
我编写了一个快速的 C# 控制台程序来构建和序列化一个对象,它与上面的变体 1 匹配。所以现在我认为问题不在于 JSON,而在于 SOAP-UI 和 Web 服务之间的握手。接下来我将编写一个 C# 测试客户端,看看那里会发生什么。
SQRT_WCF.PlaceOrder order = new SQRT_WCF.PlaceOrder();
order.ClaimID = "claim1";
string strJson = new JavaScriptSerializer().Serialize(order);
有人在 webservice 中询问属性,假设他是这个意思:
public 接口 ISQRTREST
{
[操作合约]
void DoWork();
[OperationContract]
[WebInvoke(Method="GET",
ResponseFormat = WebMessageFormat.Json ,
BodyStyle = WebMessageBodyStyle.Wrapped ,
UriTemplate ="json/{id}"
)]
string JSONData(String id);
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "placeOrder/"
)]
SQ_jsonModel.Response placeOrder(PlaceOrder order);
}
Web.Config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="SQRT_WCF.SQRTREST" behaviorConfiguration="ServiceBehaviour">
<endpoint address="" binding="webHttpBinding" bindingConfiguration="longTimeoutBinding" contract="SQRT_WCF.ISQRTREST" behaviorConfiguration="web">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="longTimeoutBinding"
receiveTimeout="00:10:00" sendTimeout="00:10:00">
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
更新:
似乎更改 BodyStyle = WebMessageBodyStyle.Bare 使其与变体 1 一起使用。
但是现在,问题是我需要能够让它与 WebMessageBodyStyle.Wrapped 一起工作。
这是生产中的现有 Web 服务。写程序的人早就不在了,现在B2B/BizTalk团队接手了这个项目。我们正在使用还没有 REST 的 BT2010,并且我们正在尝试为我们的客户提供相同的界面,因此他们无需更改 URL 以外的任何内容。
RESTful web service body format
似乎解释得相当好,我将尝试使用 C# 中的 .wrapped 选项进行变体 2。
更新 2:
尝试使用 BodyWrapped 的变体 2 仍然得到一个空对象:
我就是这样包装的。采用在 Update1 中有效的方法
string strPostBodyWrapped = "{\"PlaceOrder\" : " + strPostBody + "}";
午餐后将进行更多调试。
更新 3:
仍然没有运气。根据上面的 Whosebug 参考,包装器看起来应该是 "entity",我尝试了几种方法:
来自 SOAP-UI:
{
"entity" : {
"claimID" : "value",
}
我也试过 "Entity" 而不是 "entity",请确定。
我的 DataContract 中的所有字段都是字符串,因此序列化日期或数字应该没有问题。
来自 C# 客户端测试程序:
strPostBody = replaceSingleWithDoubleQuotes("{'ClaimID':'testvalue'}");
string strPostBodyWrapped = replaceSingleWithDoubleQuotes("{'entity': ") + strPostBody + "}";
public static string replaceSingleWithDoubleQuotes(string argText)
{
return argText.Replace("'", "\"");
}
如果我在 SOAP_UI 中尝试一些不好的事情,比如:
{
"entity" : { xxx
"claimID" : "value",
}
然后我意识到实体不是关键字而是他的名字 class...哎呀。
所以我尝试了 "Placeorder" 和 "placeorder" 作为我的案例的包装。
我得到了这样一个很好的错误:
The server encountered an error processing the request. The exception message is 'The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation 'placeOrderSQRT'. Encountered unexpected character 'x'.'...
所以我不明白为什么在这种情况下解析器会失败,但在其他情况下它不会失败,但它也不会设置任何字段值。
如果我尝试无效的字段名称,但语法正确,解析器似乎不会给出任何错误,例如"claimIDxxxxxxxx" : "value"
我认为您可能需要将对象字符串化。
var obj = {
"claimID" : "value",
"rederenceDate" : "value"
}
.......
data: '{ "argPlaceOrderJSON": ' + JSON.stringify(obj) + ' }'
我有一个类似的问题,结果我必须定义我的请求格式和正文。
试试这个:
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json)]
YourResultType placeOrder(PlaceOrder argPlaceOrderJSON);
答案埋在这里:
WCF BodyStyle WrappedRequest doesn't work for incoming JSON param?
包装器的名称不是参数类型,而是参数名称。
所以在我的例子中,包装器名称是 "order",因为我的参数名称是 "order":
SQ_jsonModel.Response placeOrderSQRT(PlaceOrder order);
以下来自 SOAP-UI 现在的作品:
{
"order" : {
"claimID" : "value",
"rederenceDate" : "value"
}
对我来说这是非常震惊的;通常我们可以更改变量名称和参数名称(只是不是 Class 名称),而对用户界面绝对没有影响。参考文献 post 展示了如何使用属性 "MessageParameter" 来设置外部名称,并且仍然允许在需要时更改内部名称。
接口:
namespace SQRT_WCF
{
[DataContract]
public class PlaceOrder
{
[DataMember]
public string claimID { get; set; }
[DataMember]
public string rederenceDate { get; set; }
}
}
调用的 C# 方法:
public SQ_jsonModel.Response placeOrder(PlaceOrder argPlaceOrderJSON)
{
...
}
我已将我的网络服务附加到工作进程,并通过代码进行跟踪。到目前为止,我所尝试的一切,变量 argPlaceOrderJSON 都是空的。
我至少尝试了三种变体:
变体 #1
{
"claimID" : "value",
"rederenceDate" : "value"
}
变化 #2:
{
"PlaceOrder" : {
"claimID" : "value",
"rederenceDate" : "value"
}
变化 #3:
{
"ns0:PlaceOrder" : {
"@xmlns:ns0" : "SQRT_WCF",
"claimID" : "value",
"rederenceDate" : "value"
}
我编写了一个快速的 C# 控制台程序来构建和序列化一个对象,它与上面的变体 1 匹配。所以现在我认为问题不在于 JSON,而在于 SOAP-UI 和 Web 服务之间的握手。接下来我将编写一个 C# 测试客户端,看看那里会发生什么。
SQRT_WCF.PlaceOrder order = new SQRT_WCF.PlaceOrder();
order.ClaimID = "claim1";
string strJson = new JavaScriptSerializer().Serialize(order);
有人在 webservice 中询问属性,假设他是这个意思:
public 接口 ISQRTREST { [操作合约] void DoWork();
[OperationContract]
[WebInvoke(Method="GET",
ResponseFormat = WebMessageFormat.Json ,
BodyStyle = WebMessageBodyStyle.Wrapped ,
UriTemplate ="json/{id}"
)]
string JSONData(String id);
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "placeOrder/"
)]
SQ_jsonModel.Response placeOrder(PlaceOrder order);
}
Web.Config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="SQRT_WCF.SQRTREST" behaviorConfiguration="ServiceBehaviour">
<endpoint address="" binding="webHttpBinding" bindingConfiguration="longTimeoutBinding" contract="SQRT_WCF.ISQRTREST" behaviorConfiguration="web">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="longTimeoutBinding"
receiveTimeout="00:10:00" sendTimeout="00:10:00">
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
更新: 似乎更改 BodyStyle = WebMessageBodyStyle.Bare 使其与变体 1 一起使用。
但是现在,问题是我需要能够让它与 WebMessageBodyStyle.Wrapped 一起工作。
这是生产中的现有 Web 服务。写程序的人早就不在了,现在B2B/BizTalk团队接手了这个项目。我们正在使用还没有 REST 的 BT2010,并且我们正在尝试为我们的客户提供相同的界面,因此他们无需更改 URL 以外的任何内容。
RESTful web service body format 似乎解释得相当好,我将尝试使用 C# 中的 .wrapped 选项进行变体 2。
更新 2: 尝试使用 BodyWrapped 的变体 2 仍然得到一个空对象:
我就是这样包装的。采用在 Update1 中有效的方法
string strPostBodyWrapped = "{\"PlaceOrder\" : " + strPostBody + "}";
午餐后将进行更多调试。
更新 3: 仍然没有运气。根据上面的 Whosebug 参考,包装器看起来应该是 "entity",我尝试了几种方法:
来自 SOAP-UI:
{
"entity" : {
"claimID" : "value",
}
我也试过 "Entity" 而不是 "entity",请确定。
我的 DataContract 中的所有字段都是字符串,因此序列化日期或数字应该没有问题。
来自 C# 客户端测试程序:
strPostBody = replaceSingleWithDoubleQuotes("{'ClaimID':'testvalue'}");
string strPostBodyWrapped = replaceSingleWithDoubleQuotes("{'entity': ") + strPostBody + "}";
public static string replaceSingleWithDoubleQuotes(string argText)
{
return argText.Replace("'", "\"");
}
如果我在 SOAP_UI 中尝试一些不好的事情,比如:
{
"entity" : { xxx
"claimID" : "value",
}
然后我意识到实体不是关键字而是他的名字 class...哎呀。 所以我尝试了 "Placeorder" 和 "placeorder" 作为我的案例的包装。
我得到了这样一个很好的错误:
The server encountered an error processing the request. The exception message is 'The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation 'placeOrderSQRT'. Encountered unexpected character 'x'.'...
所以我不明白为什么在这种情况下解析器会失败,但在其他情况下它不会失败,但它也不会设置任何字段值。
如果我尝试无效的字段名称,但语法正确,解析器似乎不会给出任何错误,例如"claimIDxxxxxxxx" : "value"
我认为您可能需要将对象字符串化。
var obj = {
"claimID" : "value",
"rederenceDate" : "value"
}
.......
data: '{ "argPlaceOrderJSON": ' + JSON.stringify(obj) + ' }'
我有一个类似的问题,结果我必须定义我的请求格式和正文。 试试这个:
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json)]
YourResultType placeOrder(PlaceOrder argPlaceOrderJSON);
答案埋在这里:
WCF BodyStyle WrappedRequest doesn't work for incoming JSON param?
包装器的名称不是参数类型,而是参数名称。
所以在我的例子中,包装器名称是 "order",因为我的参数名称是 "order":
SQ_jsonModel.Response placeOrderSQRT(PlaceOrder order);
以下来自 SOAP-UI 现在的作品:
{
"order" : {
"claimID" : "value",
"rederenceDate" : "value"
}
对我来说这是非常震惊的;通常我们可以更改变量名称和参数名称(只是不是 Class 名称),而对用户界面绝对没有影响。参考文献 post 展示了如何使用属性 "MessageParameter" 来设置外部名称,并且仍然允许在需要时更改内部名称。