13

使用 WCF Web 编程模型,可以像这样指定操作协定:

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs2={qs2}")]
XElement SomeRequest1(string qs1, string qs2);

现在,如果我们必须创建一个接受具有相同名称的参数数组的合约(在本例中为 qs1),合约就像这样......

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs1={qs2}")]
 XElement SomeRequest2(string qs1, string qs2);

当我们调用该方法时,我们会在运行时收到错误消息:

查询字符串必须具有具有唯一名称的“名称=值”对。请注意,名称不区分大小写。有关更多详细信息,请参阅 UriTemplate 的文档。

如何定义一个 HTTP 服务来公开具有一组参数的资源,而不使用松散的接口?

4

3 回答 3

27

我已经实现了一个简单的自定义 QueryStringConverter 以便您可以将 qs1 设为 string[] 然后将查询字符串变量以逗号分隔(例如http://server/service/SomeRequest?qs1=val1,val2,val3,val4

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml,
        UriTemplate = "SomeRequest?qs1={qs1}")]
XElement SomeRequest2(string[] qs1);

首先,您需要一个继承自 WebHttpBehavior 的类,以便我们可以注入我们的自定义 QueryStringConverter:

public class CustomHttpBehavior : System.ServiceModel.Description.WebHttpBehavior
{
    protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(System.ServiceModel.Description.OperationDescription operationDescription)
    {
        return new CustomQueryStringConverter();
    }
}

然后是处理 string[] 参数的 CustomQueryStringConverter:

public class CustomQueryStringConverter : System.ServiceModel.Dispatcher.QueryStringConverter
{
    public override bool CanConvert(Type type)
    {
        if (type == typeof(string[]))
        {
            return true;
        }

        return base.CanConvert(type);
    }

    public override object ConvertStringToValue(string parameter, Type parameterType)
    {
        if (parameterType == typeof(string[]))
        {
            string[] parms = parameter.Split(',');
            return parms;
        }

        return base.ConvertStringToValue(parameter, parameterType);
    }

    public override string ConvertValueToString(object parameter, Type parameterType)
    {
        if (parameterType == typeof(string[]))
        {
            string valstring = string.Join(",", parameter as string[]);
            return valstring;
        }

        return base.ConvertValueToString(parameter, parameterType);
    }
}

您需要做的最后一件事是创建行为配置扩展,以便运行时可以获取 CustomWebHttpBehavior 的实例:

public class CustomHttpBehaviorExtensionElement : System.ServiceModel.Configuration.BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new CustomHttpBehavior();
    }

    public override Type BehaviorType
    {
        get { return typeof(CustomHttpBehavior); }
    }
}

现在我们将元素添加到我们的配置扩展中,以便使用我们的 CustomWebHttpBehavior,我们使用该扩展的名称而不是<webHttp />我们的行为:

 <system.serviceModel>
   <services>
     <service name="NameSpace.ServiceClass">
       <endpoint address="" behaviorConfiguration="MyServiceBehavior"
        binding="webHttpBinding" contract="NameSpace.ServiceClass" />
     </service>
   </services>
  <behaviors>
   <endpointBehaviors>
    <behavior name="MyServiceBehavior">
      <customWebHttp/>
    </behavior>
   </endpointBehaviors>
  </behaviors>
  <extensions>
    <behaviorExtensions>
      <add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName" />
    </behaviorExtensions>
  </extensions>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
 </system.serviceModel>

您现在还可以扩展您的 CustomQueryStringConverter 以处理默认类型不处理的其他类型,例如可为空的值类型。

注意:在 microsoft connect 中记录了一个与此代码直接相关的错误。在您尝试查询转换不同类型的几乎所有情况下,该代码实际上并不适用。

http://connect.microsoft.com/VisualStudio/feedback/details/616486/bug-with-getquerystringconverter-not-being-called-by-webservicehost#tabs

请确保在浪费时间创建无法正常工作的自定义 REST 查询字符串转换器之前仔细阅读此内容。(适用于 Framework 4.0 及以下版本)。

于 2008-12-10T01:21:01.350 回答
5

要回复您对我的其他答案的评论:

您可以在查询字符串的末尾执行通配符参数,例如

[WebGet(ResponseFormat = WebMessageFormat.Xml,
        UriTemplate = "SomeRequest?qs1={*qs1}")]
XElement SomeRequest2(string qs1);

这样 qs1 字符串参数将是 qs1= 之后的整个原始查询字符串,然后您可以在代码中手动解析它。

QueryStringConverter 依赖于查询字符串的格式,因此如果不重写 QueryStringConverter 而不是我们在另一个答案中所做的小覆盖,就不可能完全按照您的意愿做事。

来自 MSDN:

通配符段必须遵循以下规则:

  • 每个模板字符串最多可以有一个命名通配符段。
  • 命名通配符段必须出现在路径的最右侧段。
  • 命名通配符段不能与同一模板字符串中的匿名通配符段共存。
  • 命名通配符段的名称必须是唯一的。
  • 命名通配符段不能有默认值。
  • 命名通配符段不能以“/”结尾。
于 2008-12-10T23:12:24.230 回答
2

请注意,在 WCF 3.5 中,您必须在以下位置指定完整的限定程序集名称:

   <extensions>
    <behaviorExtensions>
      <add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName, NOT SUFFICIENT HERE" />
    </behaviorExtensions>
  </extensions>

就像这样:SampleService.CustomBehavior,SampleService,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null

否则你会得到异常:

配置错误
描述:处理服务此请求所需的配置文件期间发生错误。请查看下面的具体错误详细信息并适当地修改您的配置文件。

解析器错误消息:配置中的元素无效。扩展名“CustomWebHttp”未在 system.serviceModel/extensions/behaviorExtensions 的集合中注册。

于 2009-12-24T10:02:57.543 回答