如何使用 Azure 函数 HTTP 绑定执行 REST API 可选字符串 URI 参数?

How to do REST API Optional string URI parameter with Azure Function HTTP Binding?

我在这上面花了一天时间,我真的认为这会是一件微不足道的事情。

围绕 HTTP 绑定 "Route" 参数、相关的 "Run" 方法签名和底层 Web API 路由机制文档,大约有十几个选项。我已经尝试了每一种,而且每一种似乎都会引发不同的错误。顺便说一句,这种反复试验特别令人沮丧且耗时。

Azure 函数绑定

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook

ASP.NET 路由约束文档

https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints

函数绑定允许您在定义该函数入口点的每个函数配置文件中定义 "Route" 字符串。它的有效工作方式与 "Route" 注释从 ASP.NET 工作的方式相同(这对我来说是新的)。因此,Azure Function 文档 link 到 Constraints 文档以供高级使用。

我们的用例并不高级,我们希望我们的函数像下面这样工作:

处理如下路线:

"route" : "/books/{bookName}"

它映射到一个带有 运行() 签名的函数,类似于:

Run(HttpRequestMessage req, string bookName, TraceWriter log)

如果没有 bookName,我们的函数将 return 所有书籍的数组。如果有书名,它将 return 该书的详细信息。标准 API 东西。

根据 "constraints" 文档,我们必须对 bookName 进行调整,使其成为可选的 URI 参数,我们有几个选项。下面是提供的两个examples/options:

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int?}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int=1033}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}

目前,以上两个选项均无法在函数中使用。绑定不允许 function.json 中的等号,运行() 方法中的任何可选参数都需要是 "last" 参数,我们没有控制权在 运行() 参数的顺序上。即使我们可以更改它们,我们也不想这样做,因为将 TraceWriter 日志作为最后一个参数是我们希望遵循的明智约定。

现在,Azure Functions 文档提供了一个可选参数示例,它看起来非常简单,但不知何故设法避开了上述特定用例。

这是从function.json出发的路线:

"route": "products/{category:alpha}/{id:int?}"

方法签名如下:

Run(HttpRequestMessage request, string category, int? id, TraceWriter log)

所以,这里的区别在于,他们标记为可选的参数具体是一个int,它是一个可以为空的类型。如果 URL 不包含 null int,那么 azure 函数运行时显然会传递它,从而绕过使 "id" 成为该方法的可选参数的要求。

如果你用字符串参数尝试同样的技巧,你会得到:

error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'

在Azure Functions中实现可选字符串参数是否有合理的解决方案?

如果是这样,请提供有关教程的提示。 如果没有,请在教程中明确指出此限制。

这适用于像 int? 这样的结构,但不适用于像 string 这样的 类。我在这里提交了一个问题:https://github.com/Azure/azure-webjobs-sdk-extensions/issues/224.

在解决此问题之前,您最好的选择可能是使用如下路由模板:"route": "books/{bookName?}",但 使用 bookName 作为参数你的函数:Run(HttpRequestMessage req, TraceWriter log)

在我的简短测试中,这将为您提供正确的路由匹配,但您必须自己对 req 参数进行一些 URI 解析以检查书名。

更新:此问题已修复,现在应该可以按预期工作。

这听起来像是两个不同的终点,因为它们根据单一职责原则做两件不同的事情

1) 获取该位置的资源列表,"books/"

Run(HttpRequestMessage req, TraceWriter log)

2) 获取特定资源,"books/{bookName}"

Run(HttpRequestMessage req, string bookName, TraceWriter log)

使用 1。我会使用查询字符串参数来传递页面大小和页码,这样我就不会 return 出于性能原因,我没有 return 该 uri 的所有资源。

string pageSizeQuery = request.GetQueryNameValuePairs()
        .FirstOrDefault(q => String.Compare(q.Key, "pagesize", StringComparison.OrdinalIgnoreCase) == 0)
        .Value;

if (!int.TryParse(pageSizeQuery, out var pageSize))
{
    pageSize = 25;
}

string pageQuery = request.GetQueryNameValuePairs()
        .FirstOrDefault(q => String.Compare(q.Key, "page", StringComparison.OrdinalIgnoreCase) == 0)
        .Value;

if (!int.TryParse(pageQuery, out var page))
{
    page = 1;
}