我正在使用System.Web.Http.RouteAttribute
和System.Web.Http.RoutePrefixAttribute
为我的Web API 2应用程序启用更干净的URL。对于我的大部分请求,我可以使用路由(例如Controller/param1/param2
)或者我可以使用查询字符串(例如Controller?param1=bob¶m2=mary
)。
不幸的是,对于我的一个控制器(并且只有一个),这会失败。这是我的控制器:
[RoutePrefix("1/Names")]
public class NamesController : ApiController
{
[HttpGet]
[Route("{name}/{sport}/{drink}")]
public List<int> Get(string name, string sport, string drink)
{
// Code removed...
}
[HttpGet]
[Route("{name}/{drink}")]
public List<int> Get(string name, string drink)
{
// Code removed...
}
}
当我使用路由请求时,两者都可以正常工作。但是,如果我使用查询字符串,它会失败,告诉我该路径不存在。
我尝试将以下内容添加到我的WebApiConfig.cs
课程&#39; Register(HttpConfiguration config)
函数(默认路由之前和之后),但它什么也没做:
config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = @"\d+" });
为了清楚起见,我希望能够做到这两点:
localhost:12345/1/Names/Ted/rugby/coke
localhost:12345/1/Names/Ted/coke
和
localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke
localhost:12345/1/Names?name=Ted&drink=coke
但遗憾的是查询字符串版本不起作用! :(
更新
我已完全删除了第二个动作,现在尝试仅使用带有可选参数的单一动作。我已将路线属性更改为[Route("{name}/{drink}/{sport?}")]
,因为Tony建议将运动视为可空,但现在由于某种原因,这会阻止localhost:12345/1/Names/Ted/coke
成为有效路线。查询字符串的行为与以前相同。
更新2 我现在在我的控制器中有一个单一的动作:
[RoutePrefix("1/Names")]
public class NamesController : ApiController
{
[HttpGet]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
// Code removed...
}
}
但是,使用查询字符串时找不到合适的路径,而使用路由方法则不行。
答案 0 :(得分:58)
我正面临着同样的问题“如何将搜索参数包含为查询字符串?”,而我正在尝试为当前项目构建一个web api。谷歌搜索后,以下工作对我来说很好:
Api控制器动作:
[HttpGet, Route("search/{categoryid=categoryid}/{ordercode=ordercode}")]
public Task<IHttpActionResult> GetProducts(string categoryId, string orderCode)
{
}
我试图通过邮递员的网址:
http://localhost/PD/search?categoryid=all-products&ordercode=star-1932
http://localhost/PD is my hosted api
答案 1 :(得分:39)
经过艰苦的琐事和谷歌搜索后,我想出了一个“修复”。我不知道这是不是理想/最佳做法/普通的错误,但它解决了我的问题。
除了我已经使用的路线属性之外,我所做的只是添加[Route("")]
。这基本上允许Web API 2路由允许查询字符串,因为这现在是有效的路由。
现在的一个例子是:
[HttpGet]
[Route("")]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
// Code removed...
}
这使localhost:12345/1/Names/Ted/coke
和localhost:12345/1/Names?name=Ted&drink=coke
都有效。
答案 2 :(得分:16)
使用属性路由,您需要指定默认值,以便它们是可选的。
[Route("{name}/{sport=Football}/{drink=Coke}")]
分配一个值将允许它是可选的,因此你不必包含它,它将传递值来指定。
我没有为此测试查询字符串,但它应该是一样的。
我只是重新阅读了这个问题而且我看到你有2个Get动词具有相同的路径,我相信这会引起冲突,因为路由不知道要使用哪个,也许使用可选参数会有所帮助。您还可以指定一个可以为null,并在方法中检查如何继续。
[Route("{name}/{sport?}/{drink?}")]
然后检查方法中的变量以查看它们是否为空并根据需要进行处理。
希望这有帮助,有些人?洛尔
如果不是这个网站,它会提供有关属性路由的更多详细信息。
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
从该网站剪辑:
可选参数和默认值您可以指定a 通过向参数添加问号,参数是可选的 是:
[Route("countries/{name?}")] public Country GetCountry(string name = "USA") { }
目前,必须在可选参数上指定默认值 行动选择要成功,但我们可以调查解除这个问题 限制。 (如果这很重要,请告诉我们。)
可以用类似的方式指定默认值:
[Route("countries/{name=USA}")] public Country GetCountry(string name) { }
可选参数'?'并且必须在之后显示默认值 参数定义中的内联约束。
答案 3 :(得分:9)
我的部分也是一个旁注。为了使queryString参数有效,您需要为方法参数提供默认值以使其成为可选。就像通常调用C#方法时一样。
[RoutePrefix("api/v1/profile")]
public class ProfileController : ApiController
{
...
[HttpGet]
[Route("{profileUid}")]
public IHttpActionResult GetProfile(string profileUid, long? someOtherId)
{
// ...
}
...
}
这允许我像这样调用端点:
/api/v1/profile/someUid
/api/v1/profile/someUid?someOtherId=123
答案 4 :(得分:3)
这是@bhargav kishore mummadireddy's答案的轻微偏差,但这是一个重要的偏差。他的回答是将查询字符串值默认为实际的非空值。这个答案将默认为空。
它允许您通过路径路由或使用查询字符串来调用控制器。实质上,它将查询字符串的默认值设置为空,这意味着它将始终被路由。
这对我很重要,因为如果未指定查询字符串,我想返回400(错误请求),而不是让ASP.NET返回“无法在此控制器上找到此方法”错误。
[RoutePrefix("api/AppUsageReporting")]
public class AppUsageReportingController : ApiController
{
[HttpGet]
// Specify default routing parameters if the parameters aren't specified
[Route("UsageAggregationDaily/{userId=}/{startDate=}/{endDate=}")]
public async Task<HttpResponseMessage> UsageAggregationDaily(string userId, DateTime? startDate, DateTime? endDate)
{
if (String.IsNullOrEmpty(userId))
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(userId)} was not specified.");
}
if (!startDate.HasValue)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(startDate)} was not specified.");
}
if (!endDate.HasValue)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(endDate)} was not specified.");
}
}
}
答案 5 :(得分:2)
使用Route("search/{categoryid=categoryid}/{ordercode=ordercode}")
可以使用 mosharaf hossain 回答的Querystrings和内联路由参数。写这个答案应该是最佳答案和最佳方式。如果您有多个获取/放置/发布/删除,则使用Route("")
将导致问题。
答案 6 :(得分:1)
由于您有[Route("{name}/{drink}/{sport?}")]
作为属性路由,因此永远不会触及此代码。
config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = @"\d+" });
因此,只有属性路线[Route("{name}/{drink}/{sport?}")]
才会在这里受到尊重。由于您的请求localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke
,在网址中没有名称,运动或饮品,因此不会与此属性路线匹配。匹配路由时,我们不考虑查询字符串参数。
要解决此问题,您需要在属性路由中设置所有3个可选项。然后它将匹配请求。
答案 7 :(得分:0)
我使用FromUri属性作为解决方案
[Route("UsageAggregationDaily")]
public async Task<HttpResponseMessage> UsageAggregationDaily([FromUri] string userId = null, [FromUri] DateTime? startDate = null, [FromUri] DateTime? endDate = null)