Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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).

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:

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. 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. Query strings are behaving the same way as before.

Update 2 I now have a singular action in my controller:

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

like image 311
Chris Paton Avatar asked Mar 25 '14 18:03

Chris Paton


People also ask

What is the limitation of query string?

The default limit is 16,384 characters (yes, Microsoft's web server accepts longer URLs than Microsoft's web browser). This is configurable. Up to 8,000 bytes will work.

How do I run a query string?

A Query String Collection is used to retrieve the variable values in the HTTP query string. If we want to transfer a large amount of data then we can't use the Request. QueryString. Query Strings are also generated by form submission or can be used by a user typing a query into the address bar of the browsers.

What is the use of attribute routing?

As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily create URIs that describe hierarchies of resources. The earlier style of routing, called convention-based routing, is still fully supported.


Video Answer


5 Answers

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. After googling, the following is working fine for me:

Api controller action:

[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
like image 140
Mosharaf Hossain Avatar answered Oct 21 '22 23:10

Mosharaf Hossain


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. This basically allows Web API 2 routing to allow query strings, as this is now a valid Route.

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.

like image 20
Chris Paton Avatar answered Oct 21 '22 23:10

Chris Paton


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. You can also specify one can be null and do checking in the method as to how to proceed.

[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

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.

like image 22
Tony Avatar answered Oct 21 '22 22:10

Tony


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. Just as you would also do when normally invoking a C# method.

[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
like image 12
Juri Avatar answered Oct 21 '22 22:10

Juri


Using Route("search/{categoryid=categoryid}/{ordercode=ordercode}") will enable you to use both Querystrings and inline route parameters as answered by 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.

like image 6
Bhargava Mummadireddy Avatar answered Oct 22 '22 00:10

Bhargava Mummadireddy