使用属性路由时查询字符串不起作用

时间:2014-03-25 18:08:39

标签: c# asp.net-web-api query-string asp.net-web-api-routing attributerouting

我正在使用System.Web.Http.RouteAttributeSystem.Web.Http.RoutePrefixAttribute为我的Web API 2应用程序启用更干净的URL。对于我的大部分请求,我可以使用路由(例如Controller/param1/param2)或者我可以使用查询字符串(例如Controller?param1=bob&param2=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...
    }
}

但是,使用查询字符串时找不到合适的路径,而使用路由方法则不行。

8 个答案:

答案 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/cokelocalhost: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)