81

I have a controller with the following signature:

[Route("products/filter/{apc=apc}/{xpc=xpc}/{sku=sku}")]
public IHttpActionResult Get(string apc, string xpc, int? sku)
{ ... }

I call this method with following URIs:

  • ~/api/products/filter?apc=AA&xpc=BB
  • ~/api/products/filter?sku=7199123

The first URI works without issue. The second one has a strange side effect. Even though the default values for apc and xpc should be null when not provided, the parameters are actually their names. I can overcome this by adding the additional logic:

apc = (apc == "apc") ? null : apc;
xpc = (xpc == "xpc") ? null : xpc;

This seems like a hack, and would be problematic if value passed was ever equal to the parameter name.

Is there a way to define the Route without this side effect?

Josh
  • 8,219
  • 13
  • 76
  • 123
  • 3
    You provided default values in your route template. Why would you expect them to be `null`? – Kenneth K. Jul 07 '16 at 15:16
  • I thought {sku=sku} mapped the parameter in the query string to the method parameter. – Josh Jul 07 '16 at 15:19
  • 4
    You should review the [documentation for attribute routing](http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#optional). It shows how to make parameters optional. – Kenneth K. Jul 07 '16 at 15:20

4 Answers4

175

I figured it out. I was using a bad example I found in the past of how to map query string to the method parameters.

In case anyone else needs it, in order to have optional parameters in a query string such as:

  • ~/api/products/filter?apc=AA&xpc=BB
  • ~/api/products/filter?sku=7199123

you would use:

[Route("products/filter/{apc?}/{xpc?}/{sku?}")]
public IHttpActionResult Get(string apc = null, string xpc = null, int? sku = null)
{ ... }

It seems odd to have to define default values for the method parameters when these types already have a default.

Josh
  • 8,219
  • 13
  • 76
  • 123
  • I echo your thoughts - `It seems odd to have to define default values for the method parameters when these types already have a default.`. Checked the details on MSDN als [here](https://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#optional) but it doesn't talk about any technical details as to why it has been decided to be like that. – RBT Feb 02 '17 at 10:48
  • 42
    Nullability and optionality are two different concepts, and they have nothing to do with one another (since nullable values can be used outside a parameter list). Simply declaring a parameter `int?` marks it as *nullable*, but it is not *optional*: You must pass either a non-null integral value or `null`. Adding `= null`, however, is the syntax required for declaring the default value of an optional parameter. Hence, `int? sku = null` is a nullable `int` whose default value is `null` (instead of some other integral value). – Mike Hofer Sep 21 '17 at 15:31
  • @MikeHofer I feel like you've lost some upvotes by not making your excellent comment an answer. And like [Whose Line is it Anyway?](https://www.youtube.com/watch?v=1gl__LLKMIs), that's why we're here, right? – ruffin Feb 02 '23 at 19:59
  • 1
    @ruffin Alas, you're likely correct. But that comment is from 5 years ago, and any chance at scoring that sweet, sweet rep is likely long gone. – Mike Hofer Feb 03 '23 at 01:48
36

you need only set default value to parameters(you do not need the Route attribute):

public IHttpActionResult Get(string apc = null, string xpc = null, int? sku = null)
{ ... }
Ali Rasouli
  • 1,705
  • 18
  • 25
8

Sku is an int, can't be defaulted to string "sku". Please check Optional URI Parameters and Default Values.

John Smith
  • 7,243
  • 6
  • 49
  • 61
Petre T
  • 472
  • 5
  • 12
  • Not recommended solution as you need to provide additional validation logic to check whether the parameter is actually an `int` if it is not `null`. If you stick to `int?` the framework provides the validation out-of-the-box and you only need to check the `ModelState`... – Bozhidar Stoyneff Oct 27 '18 at 16:01
1
[Route("~/api/[Controller]/AutocompleteAdress/{input=}/{input2=}")]
public IEnumerable<string> GetAutocompleteAdress(string input, string input2)

It works for me (ASP.NET WEB API).

John Smith
  • 7,243
  • 6
  • 49
  • 61
userx
  • 11
  • 1
  • This seems to work pretty well in my usage as well. However, do you have reference to this syntax anywhere? Also, it does seem to stuff up the default `ModelState` a bit if you don't supply the parameters. E.g. I tried with `int orgId` & the `ModelState` had both `orgId` and `orgId.Int32` when it wasn't supplied by the user. However `{orgId:int=}` did not work. – Lovethenakedgun Oct 17 '22 at 17:17