4

I've just switched from AttributeRouting to WebApi 2.0 AttributeRouting, and have got a controller and action defined like so:

public class InvitesController : ApiController
{
    [Route("~/api/invites/{email}")]
    [HttpGet]
    [ResponseType(typeof(string))]
    public IHttpActionResult InviteByEmail(string email)
    {
        return this.Ok(string.Empty);
    }
}

Example query:

GET: http://localhost/api/invites/test@foo.com

The response I receive is a 200, with empty content (due to string.Empty).


This all works fine -- but I want to change the email property to be a query parameter instead . So I update the controller to:

public class InvitesController : ApiController
{
    [Route("~/api/invites")]
    [HttpGet]
    [ResponseType(typeof(string))]
    public IHttpActionResult InviteByEmail(string email)
    {
        return this.Ok(string.Empty);
    }
}

But now when querying the endpoint with:

GET: http://localhost/api/invites?email=test@foo.com

The response I receive is a 404:

{
"message": "No HTTP resource was found that matches the request URI 'http://localhost/api/invites?email=test@foo.com'.",
"messageDetail": "No route providing a controller name was found to match request URI 'http://localhost/api/invites?email=test@foo.com'"
}

Does anyone know why it doesn't match the route when the parameter is swapped to a query parameter, rather than inline of the url ?


As requested, WebApiConfig is defined like so:

public static void Register(HttpConfiguration config)
{
    var jsonFormatter = config.Formatters.JsonFormatter;
    jsonFormatter.Indent = true;
    jsonFormatter.SerializerSettings.ContractResolver = new RemoveExternalContractResolver();

    config.MapHttpAttributeRoutes();
}

Thanks !

tris
  • 1,780
  • 3
  • 18
  • 28
  • You haven't really made your intention clear (what you're trying to do) you are using POST, are you actually POSTing any information in the Request Body? or is this method really a GET? – ry8806 Nov 13 '13 at 15:20
  • Sorry, swapping to a HttpGet makes no difference in the 404 when the email is passed via a query parameter. I'll update the question. – tris Nov 13 '13 at 15:29
  • I just now tried your scenario and I am unable to repro it. Could you share how your WebApiConfig.cs looks like? – Kiran Nov 13 '13 at 15:41
  • Added to the end of the question. – tris Nov 13 '13 at 15:45
  • Ok - I think I figured it out. It seems to be a clash with another route I have defined. If I change the Route definition to "~/api/invites/create", and call it using /api/invites/create?email=test@foo.com, it seems to work fine. As to which endpoint it is clashing against, I'll have to dig deeper. It would be nice for the response to be a bit more clearer there :( – tris Nov 13 '13 at 15:54
  • Would it be possible for you to share a repro of this? We would like to see how this experience can be improved. – Kiran Nov 13 '13 at 15:59
  • Added an answer with the repro steps (unsure of best practice to sharing this as I am new to posting). http://stackoverflow.com/questions/19956974/webapi-2-0-routes-not-matching-with-query-parameters/19959182#19959182 – tris Nov 13 '13 at 16:30
  • Thanks! Tris...I have filed an issue regarding it here: https://aspnetwebstack.codeplex.com/workitem/1417 – Kiran Nov 13 '13 at 19:23

3 Answers3

6

I think you need to include the query parameter (along with its type) in the Route as follows:

[Route("api/invites/{email:string}")]

using it will be

POST: http://localhost/api/invites/test@foo.com

Alternatively if you want to name the query parameter:

[Route("api/invites")]

using it will be (as long as you have an email parameter in your method)

POST: http://localhost/api/invites?email=test@foo.com

As you comment in edhedges answer: the route template cannot start with a '/' or a '~', so you can remove that from the route as above

ry8806
  • 2,258
  • 1
  • 23
  • 32
  • I can get the first one to resolve ([Route("api/invites/{email:string}")]) (with or without the type). It's the second one that doesn't ([Route("api/invites")]), with the email parameter in the method. This just returns the 404 listed in the initial question. – tris Nov 13 '13 at 15:29
  • @Tris because of IIS file routing, IIS is trying to match the .com in the the email address to a file. Without IIS config changes you can only use email address in the querystring. See http://stackoverflow.com/a/12847224/324497 for the config changes to use email address in the route. – DalSoft Sep 18 '14 at 12:29
3

Issue is a clash of route definitions, which went unnoticed due to being cross-controller (and also some of the routes being 'absolute' (~/)). Below is an example that reproduces the result.

public class ValuesController : ApiController
{
    [Route("~/api/values")]
    [HttpGet]
    public IHttpActionResult First(string email)
    {
        return this.Ok("first");
    }
}

[RoutePrefix("api/values")]
public class ValuesTwoController : ApiController
{
    [Route("")]
    [HttpGet]
    public IHttpActionResult Second(string email)
    {
        return this.Ok("second");
    }
}

Issuing a request:

GET: http://localhost/api/values?email=foo

Will return a 404 with a response of:

{
    "message": "No HTTP resource was found that matches the request URI 'http://localhost/api/values?email=foo'.",
    "messageDetail": "No route providing a controller name was found to match request URI 'http://localhost/api/values?email=foo'"
}

What is misleading is the response message.

tris
  • 1,780
  • 3
  • 18
  • 28
2

I think you need to change your Route declaration to this: [Route("~/api/invites?{email}")]

Here is a relevant link: http://attributerouting.net/#route-constraints

edhedges
  • 2,722
  • 2
  • 28
  • 61
  • This isn't using the 3rd Party AttributeRouting framework anymore (although from what I understand, it's based on that) -- but I did try that initially, since that is what it used to be defined as. Now in the WebApi2.0 AttributeRouting framework, attempting to add a `?` in a Route definition throws the exception. An exception of type 'System.ArgumentException' occurred in System.Web.Http.dll but was not handled in user code Additional information: The route template cannot start with a '/' or '~' character and it cannot contain a '?' character. – tris Nov 13 '13 at 15:05
  • Oh my bad. I misread your question looks like @ry8806 knows what's up though. Sorry again. – edhedges Nov 13 '13 at 15:23