简体   繁体   English

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

[英]Query string not working while using attribute routing

I'm using System.Web.Http.RouteAttribute and System.Web.Http.RoutePrefixAttribute to enable cleaner URLs for my Web API 2 application.我正在使用System.Web.Http.RouteAttributeSystem.Web.Http.RoutePrefixAttribute为我的 Web API 2 应用程序启用更清晰的 URL。 For most of my requests, I can use routing (eg. Controller/param1/param2 ) or I can use query strings (eg. Controller?param1=bob&param2=mary ).对于我的大多数请求,我可以使用路由(例如Controller/param1/param2 )或我可以使用查询字符串(例如Controller?param1=bob&param2=mary )。

Unfortunately, with one of my Controllers (and only one), this fails.不幸的是,使用我的一个控制器(并且只有一个),这失败了。 Here is my Controller:这是我的控制器:

[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...
    }
}

When I make a request to either using routing, both work fine.当我使用路由向其中一个发出请求时,两者都可以正常工作。 However, if I use a query string, it fails, telling me that that path does not exist.但是,如果我使用查询字符串,它会失败,告诉我该路径不存在。

I have tried adding the following to my WebApiConfig.cs class' Register(HttpConfiguration config) function (before and after the Default route), but it did nothing:我尝试将以下内容添加到我的WebApiConfig.cs类的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+" });

So for clarity, I would like to be able to do both this:因此,为清楚起见,我希望能够同时执行以下操作:

localhost:12345/1/Names/Ted/rugby/coke
localhost:12345/1/Names/Ted/coke

and,和,

localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke
localhost:12345/1/Names?name=Ted&drink=coke

but sadly the query string versions don't work!但遗憾的是查询字符串版本不起作用! :( :(

Updated更新

I've removed the second Action altogether and now trying to use just a singular Action with optional parameters.我已经完全删除了第二个 Action,现在尝试只使用带有可选参数的单个 Action。 I've changed my route attribute to [Route("{name}/{drink}/{sport?}")] as Tony suggested to make sport nullable, but this now prevents localhost:12345/1/Names/Ted/coke from being a valid route for some reason.我已将我的路线属性更改为[Route("{name}/{drink}/{sport?}")]作为 Tony 建议让运动为空,但这现在阻止了localhost:12345/1/Names/Ted/coke由于某种原因成为有效路线。 Query strings are behaving the same way as before.查询字符串的行为方式与以前相同。

Update 2 I now have a singular action in my controller:更新 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...
    }
}

but still, using query strings does not find a suitable path, while using the routing method does.但是,使用查询字符串仍然找不到合适的路径,而使用路由方法却可以。

I was facing the same issue of 'How to include search parameters as a query string?', while I was trying to build a web api for my current project.当我试图为我当前的项目构建一个 web api 时,我面临着同样的问题“如何将搜索参数包含为查询字符串?”。 After googling, the following is working fine for me:谷歌搜索后,以下对我来说工作正常:

Api controller action: Api 控制器动作:

[HttpGet, Route("search/{categoryid=categoryid}/{ordercode=ordercode}")]

public Task<IHttpActionResult> GetProducts(string categoryId, string orderCode)
{

}

The url I tried through postman:我通过邮递员尝试的网址:

http://localhost/PD/search?categoryid=all-products&ordercode=star-1932

http://localhost/PD is my hosted api

After much painstaking fiddling and Googling, I've come up with a 'fix'.经过艰苦的摆弄和谷歌搜索,我想出了一个“修复”。 I don't know if this is ideal/best practice/plain old wrong, but it solves my issue.我不知道这是否理想/最佳实践/完全错误,但它解决了我的问题。

All I did was add [Route("")] in addition to the route attributes I was already using.除了我已经使用的路由属性之外,我所做的只是添加[Route("")] This basically allows Web API 2 routing to allow query strings, as this is now a valid Route.这基本上允许 Web API 2 路由允许查询字符串,因为它现在是一个有效的路由。

An example would now be:现在的一个例子是:

[HttpGet]
[Route("")]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
    // Code removed...
}

This makes both localhost:12345/1/Names/Ted/coke and localhost:12345/1/Names?name=Ted&drink=coke valid.这使得localhost:12345/1/Names/Ted/cokelocalhost:12345/1/Names?name=Ted&drink=coke有效。

With the Attribute routing you need to specify default values so they would be optional.使用属性路由,您需要指定默认值,以便它们是可选的。

[Route("{name}/{sport=Football}/{drink=Coke}")]

Assigning a value will allow it to be optional so you do not have to include it and it will pass the value to specify.分配一个值将允许它是可选的,所以你不必包含它,它会传递值来指定。

I have not tested the query string for this but it should work the same.我还没有为此测试过查询字符串,但它应该可以正常工作。

I just re-read the question and I see that you have 2 Get verbs with the same path, I believe this would cause conflict as routing would not know which one to utilize, perhaps using the optional params will help.我只是重新阅读了这个问题,我看到您有 2 个具有相同路径的 Get 动词,我相信这会导致冲突,因为路由不知道要使用哪个,也许使用可选参数会有所帮助。 You can also specify one can be null and do checking in the method as to how to proceed.您还可以指定一个可以为 null 并在方法中检查如何继续。

[Route("{name}/{sport?}/{drink?}")]

Then check the variables in the method to see if they are null and handle as needed.然后检查方法中的变量,看它们是否为空,并根据需要进行处理。

Hope this helps, some?希望这有帮助,一些? lol哈哈

If not perhaps this site will, it has more details about attribute routing.如果不是,这个站点可能会,它有更多关于属性路由的详细信息。

http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2 http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Clip from that site:来自该网站的剪辑:

Optional parameters and default values You can specify that a parameter is optional by adding a question mark to the parameter, that is:可选参数和默认值您可以通过在参数上添加问号来指定参数是可选的,即:

 [Route("countries/{name?}")] public Country GetCountry(string name = "USA") { }

Currently, a default value must be specified on the optional parameter for action selection to succeed, but we can investigate lifting that restriction.目前,必须在可选参数上指定默认值才能使操作选择成功,但我们可以研究解除该限制。 (Please let us know if this is important.) (如果这很重要,请告诉我们。)

Default values can be specified in a similar way:可以用类似的方式指定默认值:

 [Route("countries/{name=USA}")] public Country GetCountry(string name) { }

The optional parameter '?'可选参数“?” and the default values must appear after inline constraints in the parameter definition.并且默认值必须出现在参数定义中的内联约束之后。

Just a side note from my part as well.也只是我的一个旁注。 In order for queryString params to work, you need to provide a default value for your method parameters to make it optional .为了使 queryString 参数起作用,您需要为方法参数提供默认值以使其成为 optional Just as you would also do when normally invoking a C# method.正如您在通常调用 C# 方法时所做的那样。

[RoutePrefix("api/v1/profile")]
public class ProfileController : ApiController
{

   ...

   [HttpGet]
   [Route("{profileUid}")]
   public IHttpActionResult GetProfile(string profileUid, long? someOtherId) 
   {
      // ...
   }

   ...

}

This allows me to call the endpoint like this:这允许我像这样调用端点:

/api/v1/profile/someUid
/api/v1/profile/someUid?someOtherId=123

Using Route("search/{categoryid=categoryid}/{ordercode=ordercode}") will enable you to use both Querystrings and inline route parameters as answered by mosharaf hossain .使用Route("search/{categoryid=categoryid}/{ordercode=ordercode}")将使您能够使用mosharaf hossain回答的查询字符串和内联路由参数。 Writing this answer as this should be top answer and best way.写这个答案,因为这应该是最佳答案和最佳方式。 Using Route("") will cause problems if you have multiple Gets/Puts/Posts/Deletes.如果您有多个 Gets/Puts/Posts/Deletes,使用Route("")会导致问题。

Here's a slight deviant of @bhargav kishore mummadireddy's answer, but an important deviation.这是@bhargav kishore mummadireddy 的回答的一个轻微偏差,但一个重要的偏差。 His answer will default the querystring values to an actual non-empty value.他的回答会将查询字符串值默认为实际的非空值。 This answer will default them to empty.这个答案会将它们默认为空。

It allows you to call the controller through path routing, or using the querystring.它允许您通过路径路由或使用查询字符串来调用控制器。 Essentially, it sets the default value of the querystring to empty, meaning it will always be routed.本质上,它将查询字符串的默认值设置为空,这意味着它将始终被路由。

This was important to me, because I want to return 400 (Bad Request) if a querystring is not specified, rather than having ASP.NET return the "could not locate this method on this controller" error.这对我很重要,因为如果未指定查询字符串,我想返回 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.");
            }
        }
    }

I use FromUri attribute as solution我使用 FromUri 属性作为解决方案

[Route("UsageAggregationDaily")]
public async Task<HttpResponseMessage> UsageAggregationDaily([FromUri] string userId = null, [FromUri] DateTime? startDate = null, [FromUri] DateTime? endDate = null)

Since you have [Route("{name}/{drink}/{sport?}")] as attribute routing, this code will never be hit.由于您将[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+" });

So only the attribute route [Route("{name}/{drink}/{sport?}")] is going to be honored here.所以只有属性路由[Route("{name}/{drink}/{sport?}")]会在这里被尊重。 Since your request localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke , doesn't have name, sport or drink in the URL it is not going to match this attribute route.由于您的请求localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke ,在 URL 中没有名称、运动或饮料,它不会匹配此属性路由。 We do not consider the query string parameters when matching the routes.我们在匹配路由时不考虑查询字符串参数。

To solve this, you need to make all 3 optional in your attribute route.要解决此问题,您需要在属性路由中将所有 3 个选项设为可选。 Then it will match the request.然后它将匹配请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM