我可以声明一个 web API 端点来接受任何参数并让我自己检查它们吗?
Can I declare a web API endpoint that accepts any arguments and lets me check them myself?
我正在开发基于 ASP.NET 的 .NET 4.6.1 Web 应用程序。我们的网络 API 控制器大量使用 [HttpGet]
和 [HttpPost]
属性;路由是通过 [Route]
属性完成的。
我了解到,如果多个方法具有相同的路由,ASP.NET 将通过它们的参数名称(和编号)来区分它们。
现在,在我的一个控制器上,我想提供一种方法(其路由,或至少路由 + HTTP 动词组合,是唯一的)将接受其查询字符串中的任何参数。最好是,我希望将解析后的查询字符串作为 key/value 对的列表,但如果我必须自己解析查询字符串,那也行。
如何在保持我们的通用代码结构的同时实现这一点(即依赖 [Route]
进行路由等)?
我找到了几个可能的解决方案,但不知何故,总是缺少某些东西,或者至少,由于缺乏文档,我的发现尚无定论。例如:
- 我知道我可能可以通过
HttpContext
访问完整的查询字符串,但这并不能解决 ASP.NET 必须首先将具有任意参数的请求映射到我的方法的路由问题。
- 我发现了一些关于模型绑定器的提示,但是从我几个月前实现模型绑定器开始,它们似乎总是一次只能处理一个参数,而不是整个查询字符串中的所有参数。 (或者也许我弄错了,迷失在提供的对象的迷宫中?)
- 我曾尝试使用
DictionaryModelBinder<string, string>
,但我从那个得到的只是一个空的 Dictionary<string, string>
。
编辑:例如,我希望我的端点(我们称它为 call
)对以下任何一项做出反应:
.../call
.../call?a=5&b=20&c=800
.../call?a=foo&z=bar
基本原理简介:该方法是基于我的实际控制器的抽象基础class。 URL 参数将被每个单独的控制器实际接受的信息可以很容易地从运行时 data/reflection 中提取,因此如果接收到的参数不合适,我可以自己 return 状态 404。我所做的 而不是 想做的是为每个控制器单独实现该方法 - 尽管这会让我在每个控制器上指定不同的参数,但它会造成不便的冗余。
EDIT2:这是控制器外观的简化版本:
基地class:
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 查询字符串中的参数映射到特定于控制器的对象的属性。我想消除为每个控制器提供单独的键 class 的必要性,而是在字典或类似的东西中接收 call
的参数。
您可以接受如下字典:
[HttpPost]
[Route("api/call")]
public void SomeMethod(HttpRequestMessage request)
{
Dictionary<string,string> requestData= request.Content.ReadAsAsync<Dictionary<string,string>>().Result;
}
然后你就可以看字典了。
您可以使用 HttpRequestMessage
作为操作方法中的参数,而不是依赖自定义模型绑定器:
[Route("call")]
public IHttpActionResult SomeMethod(HttpRequestMessage request)
{
var dict = new Dictionary<string, string>(ParseQueryStringToDictionary(request));
// some code here
}
然后您需要使用 ParseQueryStringToDictionary
方法将查询字符串解析为 Dictionary<string, string>
:
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&b=20&c=800
我正在开发基于 ASP.NET 的 .NET 4.6.1 Web 应用程序。我们的网络 API 控制器大量使用 [HttpGet]
和 [HttpPost]
属性;路由是通过 [Route]
属性完成的。
我了解到,如果多个方法具有相同的路由,ASP.NET 将通过它们的参数名称(和编号)来区分它们。
现在,在我的一个控制器上,我想提供一种方法(其路由,或至少路由 + HTTP 动词组合,是唯一的)将接受其查询字符串中的任何参数。最好是,我希望将解析后的查询字符串作为 key/value 对的列表,但如果我必须自己解析查询字符串,那也行。
如何在保持我们的通用代码结构的同时实现这一点(即依赖 [Route]
进行路由等)?
我找到了几个可能的解决方案,但不知何故,总是缺少某些东西,或者至少,由于缺乏文档,我的发现尚无定论。例如:
- 我知道我可能可以通过
HttpContext
访问完整的查询字符串,但这并不能解决 ASP.NET 必须首先将具有任意参数的请求映射到我的方法的路由问题。 - 我发现了一些关于模型绑定器的提示,但是从我几个月前实现模型绑定器开始,它们似乎总是一次只能处理一个参数,而不是整个查询字符串中的所有参数。 (或者也许我弄错了,迷失在提供的对象的迷宫中?)
- 我曾尝试使用
DictionaryModelBinder<string, string>
,但我从那个得到的只是一个空的Dictionary<string, string>
。
编辑:例如,我希望我的端点(我们称它为 call
)对以下任何一项做出反应:
.../call
.../call?a=5&b=20&c=800
.../call?a=foo&z=bar
基本原理简介:该方法是基于我的实际控制器的抽象基础class。 URL 参数将被每个单独的控制器实际接受的信息可以很容易地从运行时 data/reflection 中提取,因此如果接收到的参数不合适,我可以自己 return 状态 404。我所做的 而不是 想做的是为每个控制器单独实现该方法 - 尽管这会让我在每个控制器上指定不同的参数,但它会造成不便的冗余。
EDIT2:这是控制器外观的简化版本:
基地class:
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 查询字符串中的参数映射到特定于控制器的对象的属性。我想消除为每个控制器提供单独的键 class 的必要性,而是在字典或类似的东西中接收 call
的参数。
您可以接受如下字典:
[HttpPost]
[Route("api/call")]
public void SomeMethod(HttpRequestMessage request)
{
Dictionary<string,string> requestData= request.Content.ReadAsAsync<Dictionary<string,string>>().Result;
}
然后你就可以看字典了。
您可以使用 HttpRequestMessage
作为操作方法中的参数,而不是依赖自定义模型绑定器:
[Route("call")]
public IHttpActionResult SomeMethod(HttpRequestMessage request)
{
var dict = new Dictionary<string, string>(ParseQueryStringToDictionary(request));
// some code here
}
然后您需要使用 ParseQueryStringToDictionary
方法将查询字符串解析为 Dictionary<string, string>
:
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&b=20&c=800