微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在ASP.NET Core中创建多个WebApi委托

如何解决如何在ASP.NET Core中创建多个WebApi委托

所以我需要创建三个端点-
/ api / cities
/ api / cities /:id
/ api / cities?country =

一个端点应该是我输入-
www.mywebsite / api / cities

第二个端点应该是我输入-
www.mywebsite / api / cities / 1

第三个端点应该是我输入-
www.mywebsite / api / cities?country = canada

我尝试了以下操作:

[HttpGet("")]
public async Task<IActionResult> GetCities() {/*...*/}  // Should Get All Cities from DB

[HttpGet("{id:int}",Name="GetCityById")]
public async Task<IActionResult> GetCityById([Fromroute]int id) {/*...*/}  // Should Get just one city from DB 

[HttpGet("{country:alpha}")]
public async Task<IActionResult> GetCitiesByCountry([FromQuery]string country) {/*...*/}  // Should get all cities from country

前两个端点运行良好,但是当我尝试从使用查询参数传递的国家/地区获取所有城市时,似乎触发了第一个端点/api/cities而不是/api/cities?country=

解决方法

在找到解决方案之前,您的代码中有一个问题。在您的上一个动作中定义[HttpGet("{country:alpha}")]是要指定一个路由模板,您希望country路由参数存在于URL的路径部分(例如https://www.example.org/i/am/a/path)中。但是,当您使用string country属性标记FromQuery时,它将仅绑定查询字符串中的country,而不是路径。

指定路由模板还意味着,当您向/api/cities?country=blah发送请求时,它永远不会与您的GetCitiesByCountry动作匹配,因为它希望它以/api/cities/country的形式出现,例如这就是您使用路由模板指定的内容。因此,您需要做的第一件事就是将您的最后一个操作更改为:

[HttpGet]
public async Task<IActionResult> GetCitiesByCountry([FromQuery] string country)

现在这已成为现实,它仍然无法正常工作。问题归结为:选择操作时仅考虑请求URL的path部分。这意味着,既然我们已经删除了GetCitiesByCountry的路由模板,则/api/cities/api/cities?country=blah的请求将返回服务器500错误,因为GetCitiesGetCitiesByCountry是这些请求的匹配项,框架不知道要执行哪个请求。

要使其正常工作,我们需要使用所谓的动作约束。在这种情况下,我们希望指定GetCities仅在请求中不存在查询字符串的情况下才视为匹配项。为此,我们可以继承ActionMethodSelectorAttribute

using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Routing;

public class IgnoreIfRequestHasQueryStringAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(RouteContext routeContext,ActionDescriptor action)
    {
        return !routeContext.HttpContext.Request.QueryString.HasValue;
    }
}

然后用GetCities装饰它:

[HttpGet]
[IgnoreIfRequestHasQueryString]
public async Task<IActionResult> GetCities()

如果我们现在向/api/cities?country=blah发送请求,则GetCities将被排除在选择范围之外,因为该请求具有查询字符串,因此GetCitiesByCountry将按照我们的意愿执行。但是,如果我们向/api/cities发送请求,则可能会期望我们仍然得到模棱两可的操作选择错误,因为该请求不包含查询字符串,因此看起来GetCities和{{ 1}}仍然是请求的匹配项。但是请求实际上确实成功了,并根据需要运行GetCitiesByCountry

可以通过以下GetCities本身实现的以下remark on the IActionConstraint type来总结其原因:

动作约束的次要作用是使施加约束的动作比没有约束的动作更好地匹配。考虑两个动作,“ A”和“ B”具有相同的动作和控制器名称。动作“ A”仅允许HTTP POST方法(通过约束),而动作“ B”没有约束。如果传入请求是POST,则“ A”被认为是最佳匹配,因为它既匹配又具有约束。如果传入请求使用任何其他动词,则由于受到限制,“ A”将无效,因此“ B”是最佳匹配。

这意味着,因为我们定义了ActionMethodSelectorAttribute的自定义约束,该约束成功匹配了IgnoreIfRequestHasQueryString,因此/api/cities方法被认为是首选,因此没有歧义。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。