【问题标题】:Can I declare a web API endpoint that accepts any arguments and lets me check them myself?我可以声明一个接受任何参数并让我自己检查它们的 Web API 端点吗?
【发布时间】:2020-02-26 05:42:07
【问题描述】:

我正在开发一个基于 ASP.NET 的 .NET 4.6.1 Web 应用程序。我们的 Web API 控制器大量使用了[HttpGet][HttpPost] 属性;路由是通过[Route] 属性完成的。

我了解,如果多个方法具有相同的路由,ASP.NET 将通过参数名称(和编号)来区分它们。

现在,在我的一个控制器上,我想提供一种方法(其路由,或至少路由 + HTTP 动词组合是唯一的),它将接受其查询字符串中的任何参数。最好,我想将解析后的查询字符串作为键/值对的列表,但如果我必须自己解析查询字符串,那也可以。

我怎样才能做到这一点,同时保持我们的通用代码结构(即依赖[Route] 进行路由等)?

我找到了几个可能的解决方案,但不知何故,总是缺少一些东西,或者至少,由于缺乏文档,我的发现是不确定的。例如:

  • 我知道我可能可以通过HttpContext 访问完整的查询字符串,但这并不能解决 ASP.NET 必须首先将带有任意参数的请求映射到我的方法的路由问题。
  • 我发现了一些关于模型绑定器的提示,但是从几个月前我实现模型绑定器时开始,它们似乎总是一次只能处理一个参数,而不是整个查询字符串中的所有参数。 (或者也许我弄错了,迷失在提供的物品的迷宫中?)
  • 我曾尝试使用DictionaryModelBinder<string, string>,但我从中得到的只是一个空的Dictionary<string, string>

编辑:例如,我希望我的端点(我们称之为call)对以下任何一个做出反应:

.../call
.../call?a=5&b=20&c=800
.../call?a=foo&z=bar

简要原理:该方法位于我的实际控制器的抽象基类上。可以很容易地从运行时数据/反射中提取每个单独的控制器实际接受哪些 URL 参数的信息,因此如果收到的参数不合适,我可以自己返回状态 404。我想要做的是为每个控制器单独实现该方法 - 即使这会让我在每个控制器上指定不同的参数,它也会造成不方便的冗余。

EDIT2:这是控制器外观的简化版本:

基类:

public abstract class ControllerBase<T, TKey> : ApiControllerBase
{
    [Route("call")]
    [HttpGet]
    public T Call([FromUri(Name = "")] TKey key)
    {
        // retrieve T from database based on key (for instance)
    }
}

还有一个实际控制人:

public class Thing
{}

public class ThingKey
{
    public int OwnerId { get; set; }
    public int Id { get; set; }
}

[RoutePrefix("api/thing")]
public class ThingController : ControllerBase<Thing, ThingKey>
{}

还有另一个控制器:

public class Stuff
{}

public class StuffKey
{
    public int Scope { get; set; }
    public int Group { get; set; }
    public int UniqueKey { get; set; }
}

[RoutePrefix("api/stuff")]
public class StuffController : ControllerBase<Stuff, StuffKey>
{}

从这里可以看出,我的call 端点当前仍将参数从其 URL 查询字符串映射到控制器特定对象的属性。我想取消为每个控制器提供单独的键类的必要性,而是在字典或类似文件中接收call 的参数。

【问题讨论】:

  • 您介意粘贴您的控制器代码吗?你有类似 Call([FromQuery] queryParam) 的东西吗?你在使用 .Net Core 吗? docs.microsoft.com/en-us/aspnet/core/web-api/…
  • @ZeinSleiman:我已更新问题以包含一些示例代码。

标签: asp.net .net asp.net-web-api routes query-string


【解决方案1】:

您可以在操作方法中使用 HttpRequestMessage 作为参数,而不是依赖自定义模型绑定器:

[Route("call")]
public IHttpActionResult SomeMethod(HttpRequestMessage request)
{
    var dict = new Dictionary<string, string>(ParseQueryStringToDictionary(request));

    // some code here
}

然后您需要使用 ParseQueryStringToDictionary 方法将查询字符串解析为 Dictionary&lt;string, string&gt;

private IDictionary<string, string> ParseQueryStringToDictionary(HttpRequestMessage request)
{
    var queryString = string.Join(string.Empty, request.RequestUri.ToString().Split('?').Skip(1));
    var queryStringValues = System.Web.HttpUtility.ParseQueryString(queryString);
    return queryStringValues.Cast<string>().ToDictionary(x => x, x => queryStringValues[x]);
}

并称它为:.../call?a=5&amp;b=20&amp;c=800

【讨论】:

  • Shan Nautiyal's answer 是第一个,所以我接受了那个,但赞成你的。
  • @O.R.Mapper 够公平的。
【解决方案2】:

您可以接受如下字典:

[HttpPost]
[Route("api/call")]
public void SomeMethod(HttpRequestMessage request)
{
    Dictionary<string,string> requestData= request.Content.ReadAsAsync<Dictionary<string,string>>().Result;
}

然后您就可以阅读字典了。

【讨论】:

  • 谢谢,我下次上班的时候试试这个。不过,只是为了澄清一下:Content 不是请求正文吗?我需要通过 URL 传递参数。
  • 事实证明这实际上是正确的解决方案:如果路由上存在带有HttpRequestMessage 参数的方法,它显然会捕获与路由上的任何其他方法签名不匹配的所有请求相同的路线。我正在使用GetQueryNameValuePairs method 从 URL 中提取参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-11
  • 1970-01-01
  • 2011-09-26
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 2019-03-26
相关资源
最近更新 更多