ASP.NET Web API 中的可选查询字符串参数

Posted

技术标签:

【中文标题】ASP.NET Web API 中的可选查询字符串参数【英文标题】:Optional query string parameters in ASP.NET Web API 【发布时间】:2012-08-05 10:02:40 【问题描述】:

我需要实现以下 WebAPI 方法:

/api/books?author=XXX&title=XXX&isbn=XXX&somethingelse=XXX&date=XXX

所有查询字符串参数都可以为空。也就是说,调用者可以指定从 0 到全部 5 个参数。

MVC4 测试版中,我曾经做过以下事情:

public class BooksController : ApiController

    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks(string author, string title, string isbn, string somethingelse, DateTime? date) 
    
        // ...
    

MVC4 RC 不再像这样了。如果我指定的参数少于 5 个,它会回复 404 说:

在控制器“Books”上找不到与请求匹配的操作。

什么是正确的方法签名,使其表现得像以前一样,而不必在 URL 路由中指定可选参数?

【问题讨论】:

让 [httpget] 行动起来。 如果我设置了所有参数,方法就会被调用;此外,它以Get 开头,因此它会自动与HTTP GET 方法绑定... 这就是 web api 路由的工作原理,asp.net/web-api/overview/web-api-routing-and-actions/… 是的。我知道它是如何工作的。在这种特殊情况下,我无法让它工作。 这是怎么编译的? string? 不是有效类型。您不能将 string 声明为可空类型,因为它是引用类型。 【参考方案1】:

此问题已在 MVC4 的常规版本中得到修复。 现在你可以这样做了:

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 

    // ...

一切都会开箱即用。

【讨论】:

我可以在这里使用 null 作为默认值吗?例如:字符串 author=null ? 是的,null 被认为是 constant expression,因此是 valid default value。 我想知道为什么我们必须提及默认值,即使是可选参数,如 here 所说。 C# 中的任何类型始终具有默认值,因此如果路由运行时没有从 URI 接收到该类型的默认值,它可能会采用该类型的默认值。这背后的技术原因是什么?我确定这与模型绑定器有关。 @RBT 这样可以匹配路由 我正在使用日期参数,如果我只是将它们设置为可为空,则无法正常工作。所以我必须将它设置为可空并将空设置为默认值,并相应地使用服务器端验证并返回错误消息。它奏效了。【参考方案2】:

按照 vijay 的建议,可以将多个参数作为单个模型传递。当您使用 FromUri 参数属性时,这适用于 GET。这告诉 WebAPI 从查询参数中填充模型。

结果是一个更简洁的控制器动作,只有一个参数。更多信息请见:http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

public class BooksController : ApiController
  
    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks([FromUri]BookQuery query)
    
      // ...
    
  

  public class BookQuery
  
    public string Author  get; set; 
    public string Title  get; set; 
    public string ISBN  get; set; 
    public string SomethingElse  get; set; 
    public DateTime? Date  get; set; 
  

它甚至支持多个参数,只要属性不冲突。

// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query, [FromUri]Paging paging)

  // ...


public class Paging

  public string Sort  get; set; 
  public int Skip  get; set; 
  public int Take  get; set; 

更新: 为了确保这些值是可选的,请确保对模型属性使用引用类型或可为空值(例如 int?)。

【讨论】:

是的,但是单独的 [FromUri] 装饰器似乎不支持可选参数。 @JohnMeyer 您是正确的,使用 [FromUri] 并没有直接回答原始问题。它基本上说用 Uri 中的值填充这些模型。模型属性需要可以为空或引用类型,以便它们支持可选。添加了其他信息。 @AndrewC - 您能否详细说明何时/为什么需要使用可空值来确保值是可选的?如果您不使值可以为空(例如,属性int Skip)并且没有指定该属性的查询参数,API 控制器方法仍将成功匹配请求,Skip 的值将只是该类型的默认值,在这种情况下为 0 @Clark - 如果不使用可为空的类型,您将不知道用户是否没有提供值并获得未初始化的类型值(0 表示 int)或者用户是否指定了 0。通过使用可为空您确定用户未定义它,因此您可以安全地在控制器操作中应用您的默认值。如果您从上面的示例中查看 Take,如果该操作收到 Take 的 0,应该怎么做?用户的意思是请求 0 条记录还是他们没有指定它,因此您应该获取所有记录。通常,如果您希望值类型(int、bool 等)是可选的,那么它应该可以为空。【参考方案3】:

对所有参数使用初始默认值,如下所示

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 

    // ...

【讨论】:

这是正确的过程,但一方面:DateTime 不可为空。我已经尝试改用DateTime?,但是如果我在 HTTP 请求中只设置了一些参数,MVC 不会将请求映射到给定的方法。 您可以将日期作为字符串传递,并使用 DateTime.Parse() 函数在控制器函数中对其进行解析。 @MuhammadAmin, DateTime 不是 可为空 数据类型。您的代码不应编译,因为您无法将null 值分配给DateTime 类型的参数。也许,您应该将其更改为 DateTime?,或者使用不同的值作为默认值,例如 DateTime.Now @IvayloSlavov DateTime.Now 不是编译时间常数,因此不能指定为默认参数。 @GiriB,你确实是对的。 Datetime.Now 不能用于默认参数初始化,我已更正。【参考方案4】:

如果你想传递多个参数,那么你可以创建模型而不是传递多个参数。

如果你不想传递任何参数,那么你也可以跳过它,你的代码会看起来很整洁。

【讨论】:

这仅适用于请求正文中的 POST 参数 - url 中的参数仍然可以作为参数单独引用。【参考方案5】:

不能为未声明为“optional”的参数提供默认值

 Function GetFindBooks(id As Integer, ByVal pid As Integer, Optional sort As String = "DESC", Optional limit As Integer = 99)

在你的WebApiConfig

 config.Routes.MapHttpRoute( _
          name:="books", _
          routeTemplate:="api/controller/action/id/pid/sort/limit", _
          defaults:=New With .id = RouteParameter.Optional, .pid = RouteParameter.Optional, .sort = UrlParameter.Optional, .limit = UrlParameter.Optional _
      )

【讨论】:

其实可以。我使用的是 C#,而不是 VB.NET。

以上是关于ASP.NET Web API 中的可选查询字符串参数的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Web API 中带有可选参数的属性路由

ASP.NET Core,以查询字符串为模板的 Web API RouteAttribute

Web Api 属性路由中的可选参数

ASP.NET MVC 2 中带有约束的可选路由参数?

在 ASP.NET Core Web Api 中有多个带有多个查询字符串参数的 get 方法

与实体框架 ASP.net MVC 中多个表的可选关系