在WCF Web编程模型中,如何编写具有一组查询字符串参数(即具有相同名称)的操作协定?

发布于 2024-07-10 05:28:45 字数 700 浏览 15 评论 0原文

使用 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 服务,在不诉诸松散的接口的情况下公开带有一组参数的资源?

Using the WCF web programming model one can specify an operation contract like so:

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

Now if we had to make a contract that accepts an array of parameters with the same name (in this case qs1) contract like so...

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

We get the error message at run time when we make the invocation to the method:

the query string must have 'name=value' pairs with unique names. Note that the names are case-insensitive. See the documentation for UriTemplate for more details.

How does one define an HTTP service that exposes a resource with an array of parameters without resorting to a loosey-goosey interface?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

关于从前 2024-07-17 05:28:45

我实现了一个简单的自定义 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使用该扩展名时,我们在行为中使用该扩展名而不是

 <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 查询字符串转换器不可能工作。 (适用于框架 4.0 及以下版本)。

I've implemented a simple custom QueryStringConverter so that you can make qs1 an string[] then have the query string variable be comma delimited (e.g. http://server/service/SomeRequest?qs1=val1,val2,val3,val4)

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

First you need a class that inherits from WebHttpBehavior so that we can inject our custom QueryStringConverter:

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

Then our CustomQueryStringConverter that handles string[] parameters:

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);
    }
}

The last thing you need to do is create a behavior configuration extension so that the runtime can get an instance of the CustomWebHttpBehavior:

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

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

Now we add the element to our configuration extensions so that our CustomWebHttpBehavior is used, we use the Name of that extension instead of <webHttp /> in our behavior:

 <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>

You can now also extend your CustomQueryStringConverter to handle other types that the default one doesn't, such as nullable value types.

NOTE: There is a bug logged at microsoft connect that directly relates to this code. The code does not actually work in almost all circumstances where you attempt to Query Convert different types.

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

Please make sure you read this carefully before wasting hours of your time creating custom REST query string converters that cannot possibly work. (Applies to Framework 4.0 and below).

彩虹直至黑白 2024-07-17 05:28:45

为了回应您对我的其他答案的评论:

您可以在查询字符串的末尾添加通配符参数,这样

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

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

QueryStringConverter 依赖于查询字符串的格式,因此如果不重写 QueryStringConverter 而不是我们在其他答案中所做的小覆盖,就不可能完全按照您想要的方式执行某些操作。

来自 MSDN:

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

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

To respond to your comment on my other answer:

You can do a wildcard parameter at the end of the querystring like

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

This way the qs1 string parameter will be the whole raw querystring after the qs1=, you could then parse that manually in your code.

The QueryStringConverter relies on the formatting of the querystring so doing something exactly how you want is not possible without possibly rewriting QueryStringConverter instead of the little overrides we did in the other answer.

From MSDN:

Wildcard segments must follow the following rules:

  • There can be at most one named wildcard segment for each template string.
  • A named wildcard segment must appear at the right-most segment in the path.
  • A named wildcard segment cannot coexist with an anonymous wildcard segment within the same template string.
  • The name of a named wildcard segment must be unique.
  • Named wildcard segments cannot have default values.
  • Named wildcard segments cannot end with “/”.
旧瑾黎汐 2024-07-17 05:28:45

请注意,在 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 的集合中注册。

Be aware that in WCF 3.5 you must specify the full qualified assembly name in:

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

Just like this: SampleService.CustomBehavior, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Otherwise you will get exception:

Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Invalid element in configuration. The extension name 'CustomWebHttp' is not registered in the collection at system.serviceModel/extensions/behaviorExtensions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文