WCF 复杂 JSON INPUT 错误(QueryStringConverter 不可转换)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5241661/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WCF Complex JSON INPUT Error (not convertible by QueryStringConverter)
提问by Boden
Im having issue geting Complex JSON working as a parameter in my WCF Service.
我在将复杂 JSON 用作 WCF 服务中的参数时遇到问题。
Using, Microsoft.Net 3.5 SP1 in Visual Studio 2008 SP1
在 Visual Studio 2008 SP1 中使用 Microsoft.Net 3.5 SP1
With the following Contract:
有以下合同:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate="GetDataUsingDataContract?composite={composite}",
BodyStyle=WebMessageBodyStyle.Wrapped,
RequestFormat=WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract]
public class CompositeType
{
string boolValue = "true";
string stringValue = "Hello ";
[DataMember]
public string BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
Using the following URL:
使用以下网址:
http://localhost:1122/Service1.svc/GetDataUsingDataContract?composite={"BoolValue":"True","StringValue":"Hello"}
With the Enpoint Configuration:
使用端点配置:
<system.serviceModel>
<services>
<service name="WebHTTPBindingExample.Service1" behaviorConfiguration="WebHTTPBindingExample.Service1Behavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8731/Design_Time_Addresses/WebHTTPBindingExample/Service1/"/>
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<!--<endpoint address="" binding="wsHttpBinding" contract="WebHTTPBindingExample.IService1">
--><!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
--><!--
<identity>
<dns value="localhost"/>
</identity>
</endpoint>-->
<endpoint address="Web" behaviorConfiguration="ChatAspNetAjaxBehavior" binding="webHttpBinding" name="ajaxEndpoint" contract="WebHTTPBindingExample.IService1"/>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WebHTTPBindingExample.Service1Behavior">
<!-- 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="ChatAspNetAjaxBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
I Get the follwoing Error:
我收到以下错误:
Operation 'GetDataUsingDataContract' in contract 'IService1' has a query variable named 'composite' of type 'WebHTTPBindingExample.CompositeType', but type 'WebHTTPBindingExample.CompositeType' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.
回答by David Hoerster
I don't believe you're allowed to pass complex types on the query string using WCF this way. See this responsefrom a Microsoft tech in the ASP.NET forums - it's similar to your situation.
我不相信您可以通过这种方式使用 WCF 在查询字符串上传递复杂类型。在 ASP.NET 论坛中查看Microsoft 技术人员的回复- 这与您的情况类似。
Based on how you've stubbed out your service method, it seems like a more appropriate verb to use would be POST or PUT, and you could put your CompositeTypepayload in the request body. But I'm guessing as to your intention.
根据您如何删除服务方法,似乎更合适的动词是 POST 或 PUT,您可以将CompositeType有效负载放在请求正文中。但我猜你的意图。
I was able to make your code work and still use a GET by changing the query string type from CompositeTypeto Stringand then deserializing the JSON string into a CompositeTypeby using the ASP.NET JavaScriptSerializerclass. (You can use your favorite JSON helper class here -- I'm partial to JSON.NET, but I also hear FlexJsonis very good, too.)
通过将查询字符串类型从CompositeTypeto更改为String,然后CompositeType使用 ASP.NETJavaScriptSerializer类将 JSON 字符串反序列化为 a ,我能够使您的代码正常工作并仍然使用 GET 。(你可以在这里使用你最喜欢的 JSON 帮助类——我偏爱JSON.NET,但我也听说FlexJson也非常好。)
I didn't touch your web.config (except to make it work in my local test app). My only change was in the service method signature and the implementation of the service method.
我没有接触你的 web.config(除了让它在我的本地测试应用程序中工作)。我唯一的变化是服务方法签名和服务方法的实现。
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "GetDataUsingDataContract?composite={composite}",
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
CompositeType GetDataUsingDataContract(String composite);
// TODO: Add your service operations here
}
public class Service1 : IService1
{
public CompositeType GetDataUsingDataContract(String composite)
{
//use the JavaScriptSerializer to convert the string to a CompositeType instance
JavaScriptSerializer jscript = new JavaScriptSerializer();
CompositeType newComp = jscript.Deserialize<CompositeType>(composite);
newComp.StringValue += " NEW!";
return newComp;
}
}
I hope this helps. Let me know if you have other questions with this.
我希望这有帮助。如果您对此有其他疑问,请告诉我。
回答by James Roeiter
David Hoerster - your link is from 2009 , which is today invalid .
David Hoerster - 您的链接来自 2009 年,今天无效。
If you look into the following documentation : http://msdn.microsoft.com/en-us/library/bb412179(v=vs.90).aspx
如果您查看以下文档:http: //msdn.microsoft.com/en-us/library/bb412179(v=vs.90).aspx
You can see that in order to invoke
你可以看到为了调用
MyOperation(int number,Person p)
you can do the following :
您可以执行以下操作:
http://example.com/myservice.svc/MyOperation?number=7&p={"name":"John","age":42}
Davids suggestions perhaps simplifies things alot on the programming side BUT it is against all REST rules which are all about method signatures that document themselves.
Davids 的建议可能在编程方面简化了很多事情,但它违反了所有关于记录自身的方法签名的 REST 规则。
Also , be sure that your binding is webHttpBinding to convert from SOAP to REST.
此外,请确保您的绑定是 webHttpBinding,以便从 SOAP 转换为 REST。
回答by Akira Yamamoto
Just use the WebGet attribute without parameters:
只需使用不带参数的 WebGet 属性:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
On the Web.config, use enableWebScript and webHttpBinding:
在 Web.config 上,使用 enableWebScript 和 webHttpBinding:
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="Application.Service1">
<endpoint address="json" behaviorConfiguration="jsonBehavior" binding="webHttpBinding" contract="Application.IService1" />
<endpoint address="soap" binding="basicHttpBinding" contract="Application.IService1" />
</service>
</services>
</system.serviceModel>
</configuration>
Then you can call your JSON REST like this:
然后你可以像这样调用你的 JSON REST:
http://localhost:29075/Service1.svc/json/GetDatausingDataContract?composite={ "BoolValue": true, "StringValue": "Akira"}
Note: with this configuration you can use both JSON REST and SOAP.
注意:通过此配置,您可以同时使用 JSON REST 和 SOAP。
回答by ワイきんぐ
[NEW ANSWER (2019]
[新答案(2019)
In case somebody is still searching for this (I just did, and I figured out a good solution).
如果有人仍在寻找这个(我刚刚做了,我想出了一个很好的解决方案)。
In contract file:
在合同文件中:
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, UriTemplate = "TestBlockHeight?blockHeight={blockHeight}")]
Task<string> TestBlockHeight(BlockHeight blockHeight);
In service file:
在服务文件中:
public async Task<string> TestBlockHeight(BlockHeight blockHeight)
{
return await Task.FromResult($"Called TestBlockHeight with parameter {blockHeight}");
}
My custom BlockHeighttype class:
我的自定义BlockHeight类型类:
[TypeConverter(typeof(MyBlockHeightConverter))]
public class BlockHeight : IComparable<ulong>
{
private ulong value;
public BlockHeight(ulong blockHeight)
{
value = blockHeight;
}
}
And the custom TypeConverterclass that I needed to create:
TypeConverter我需要创建的自定义类:
public class MyBlockHeightConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if(destinationType == typeof(string) && value is BlockHeight blockHeight)
{
return blockHeight.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if(value is string str)
{
return new BlockHeight(ulong.Parse(str));
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if(sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
}
This works because WCF uses a QueryStringConverterto serialize url parameters, and only specific types are allowed. One of these types is an arbitrary type which is decorated with TypeConverterAttribute. More info here:
https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.dispatcher.querystringconverter?view=netframework-4.8
这是有效的,因为 WCF 使用 aQueryStringConverter来序列化 url 参数,并且只允许特定类型。其中一种类型是用 装饰的任意类型TypeConverterAttribute。更多信息:https:
//docs.microsoft.com/en-us/dotnet/api/system.servicemodel.dispatcher.querystringconverter?view=netframework-4.8

