44

PLEASE NOTE: This question was asked in 2016. The original answer to this problem was to update the microsoft api versiong package. In the current days, the problem reoccurs, but for other reasons.

Original Question:


i have some problems with the routing in asp.net core (web api).

I have this Controller (simplified):

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[Controller]")]
public class DocumentController : Controller
{

[HttpGet("{guid}", Name = "GetDocument")]
public IActionResult GetByGuid(string guid)
{
    var doc = DocumentDataProvider.Find(guid);
    if (doc == null)
        return NotFound();

    return new ObjectResult(doc) {StatusCode = 200};
}


[HttpPost]
public IActionResult Create([FromBody] Document doc)
{
    //... Creating Doc

    // Does not work   
    var val = CreatedAtRoute("GetDocument", new {guid = doc.Guid.ToString("N")}, document);

    // or this: 
    CreatedAtRoute("GetDocument", new { controller = "Document", guid = doc.Guid.ToString("N")}, document);
    // neither this
    var val = CreatedAtRoute("GetDocument", new { version = "1", controller = "Document", guid = doc.Guid.ToString("N")}, document);

    return val;
}
}

If i call Create, the document is created and the routing object was created but i get the error "No route matches the supplied values" and get a 500 status.

I can call the GetByGuid directly, without any problems.

I couldn't find any debugging help for asp.net core (like any existing routing debugger).

I would appreciate any help!

EDIT Looks like it would be a bug from microsoft's versioning package.. if i define the fix route /api/v1/[Controller] it's working.

But that's not a solution for me.

Benjamin Schäublin
  • 1,526
  • 1
  • 16
  • 26

15 Answers15

46

ASP.net core 3

Why this problem occurs:

As part of addressing dotnet/aspnetcore#4849, ASP.NET Core MVC trims the suffix Async from action names by default. Starting with ASP.NET Core 3.0, this change affects both routing and link generation.

See more:
ASP.NET Core 3.0-3.1 | Breaking Changes | Async suffix trimmed from controller action names

As @Chris Martinez says in this Github Issue:
.Net Core 3.0 and using CreatedAtRoute result in No route matches the supplied values. #558:

The reason for the change was not arbitrary; it addresses a different bug. If you're not affected by said bug and want to continue using the Async suffix as you had been doing.

How to solve

Re-enable it:

services.AddMvc(options =>
{
   options.SuppressAsyncSuffixInActionNames = false;
});

You should now pass the createActionName parameter including the Async suffix like this:

return CreatedAtAction("PostAsync", dto)

Jesse
  • 31
  • 4
Maicon Heck
  • 2,017
  • 1
  • 20
  • 27
28

I know this post is from 2017 but still i just faced the same problem and ended up here. And as it looks like you never found your mistake I'll write it here for anyone else that founds this post.

The problem is that when you call:

CreatedAtRoute("GetDocument", new { version = "1", controller = "Document", guid = doc.Guid.ToString("N")}, document);

You are telling the program to look for a "GetDocument" function that receives 3 parameters, in this case 3 strings but your actual "GetDocument" definition receives only 1 string that is your "guid":

[HttpGet("{guid}", Name = "GetDocument")]
public IActionResult GetByGuid(string guid)
{
    var doc = DocumentDataProvider.Find(guid);
    if (doc == null)
        return NotFound();

    return new ObjectResult(doc) {StatusCode = 200};
}

So for it to work you should have it like this:

CreatedAtRoute("GetDocument", new { guid = doc.Guid.ToString("N")}, document);

Another option would be to create a new get method with 3 strings and maybe you'll have to call it something different than "GetDocument".

Hope this helps the next one that comes looking for this :D

JohnB
  • 18,046
  • 16
  • 98
  • 110
  • 5
    My silly mistake was not being consistent with the parameter names i.e. `guid` here. Make sure they match when calling the `createdAtRoute()`! – Milo Jul 01 '20 at 15:00
  • Worked for me, did not notice the little } at the wrong place. Should be the accepted answer as it also works for the OP's question – AndreB Mar 28 '21 at 13:44
21

In my case it was a copy/paste error. The CreatedAtAction method had the wrong action name parameter. Make sure the first parameter (the actionName parameter) matches the action name (the function name). The corrected code is seen below:

enter image description here

Colin
  • 1,758
  • 1
  • 19
  • 24
  • Had the same issue but it's dotnet scaffolding which is wrong. Save my day. – JF Gagnon Dec 12 '21 at 13:58
  • 7
    But the `actionName` parameter is intended for the action where you can retrieve (GET) the just created entity. – Martin Schneider Jan 14 '22 at 15:32
  • Nice catch, save my day :D – Laurent Oct 06 '22 at 13:12
  • In my case it was because the routeValues parameter to CreatedAtAction had different property names than the action that I was referring to expected. So the Get expected a "customerId" and the routeValues object had an "id" property. – Quido Nov 23 '22 at 13:46
  • it works for both cases. Like the same name as the picture & the action to get these Specific resources. – Shihab Uddin May 26 '23 at 08:45
12

I'll answer my own question: It was really a bug in the versioning package of microsoft and it's gonna be fixed soon.

ASP.Net Core: internal routing fails with CreatedAtRoute #18

Jesse
  • 31
  • 4
Benjamin Schäublin
  • 1,526
  • 1
  • 16
  • 26
4

I just used return CreatedAtAction("ActionMethodName", dto); instead until they fix that bug.

redwards510
  • 1,802
  • 1
  • 17
  • 23
4

Another reason this error occurs is b/c when you use the CreatedAtAction to return 201, ASP.NET will add a Location header to the response with the url where the resource can be accessed. It uses the actionName to do this, so the method name needs to match a valid method on your controller. For example:

return CreatedAtAction("GetUser", new { id = user.UserId }, user);

If GetUser does not exist, the call will fail. Better practice probably to use:

return CreatedAtAction(nameof(GetUser), new { id = user.UserId }, user);

dev guide apidocs

Tim Jones
  • 985
  • 8
  • 14
4

Just in case, if you are like me, don't like to inherit from any of the in built ControllerX base classes, then you might face this issue, if you use following statement

  new CreatedAtActionResult(nameof(GetBookOf),
            nameof(BooksController),
            new {id = book.Id.ToString("N")},
            book)

It's because second parameter in the above statement, expect us to pass the constructor name without Controller suffix.

Hence, if you change like following, then it would work just fine.

  new CreatedAtActionResult(nameof(GetBookOf),
            "Books",
            new {id = book.Id.ToString("N")},
            book)
3

This worked for me... https://www.josephguadagno.net/2020/07/01/no-route-matches-the-supplied-values

my get-async was...

[HttpGet("{id}", Name = nameof(GetSCxItemAsync))]                            
public async Task<ActionResult<SCxItem>> GetSCxItemAsync(Guid id)

but in order to get around route error I had to add the following before the function declaration...

[ActionName(nameof(GetSCxItemAsync))]
Dharman
  • 30,962
  • 25
  • 85
  • 135
Davey
  • 31
  • 2
2

I had the same symptoms but the cause was something else. Basically it doesn't like the fact that my action method ends with Async. It works when I either rename the action method from GetAsync to Get, or add [ActionName("GetAsync")] to the action.

Source: https://github.com/microsoft/aspnet-api-versioning/issues/601

Neurion
  • 379
  • 6
  • 15
  • 2
    Google search led me to the question but this issue with `Async` in the method name was the actual problem (aspnet core 3.1). Also, was able to modify as `[ActionName(nameof(MyMethodAsync))]` to correspond with my `CreateAtAction(nameof(MyMethodAsync), obj, obj)` – mdisibio May 28 '20 at 02:15
2

I'm going to stick this in here for some other poor unfortunate soul as I've spent far too long working out what I was doing wrong.

This is wrong:

app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());

What this does is mask the problem by returning the default route which is [controller], when what you want it to return is [controller]\{newly generated id}.

This is correct:

return CreatedAtAction(nameof(GetUser), new { id = user.UserId }, user);

However there is an edge case where this fails, this being when user.UserId is null. I think that this fails because the id parameter is required, so cannot be null, thus the route doesn't bind. It's good that it fails because if it didn't then you would be creating a junk route, but the error message is more cryptic than perhaps it could be.

In my case I was returning the Id on the input object, which was null, not the newly generated Id on the output object.

satnhak
  • 9,407
  • 5
  • 63
  • 81
2

If your route has parameters (for example GetbookById(int id)) then you need 3 parts to the CreatedAtRoute for example:

book b = new book(); // The Created new book.
var routeValues = new { id = book.Id}; // Represents the parameter(s) to GetbookById
return CreatedAtRoute( nameof(GetbookById), routeValues, b);

More in: Created, CreatedAtAction, CreatedAtRoute Methods In ASP.NET Core Explained With Examples

Avishay
  • 305
  • 1
  • 11
1

Instead of this:

CreatedAtRoute("GetDocument", new { version = "1", controller = "Document", guid = doc.Guid.ToString("N")}, document);

Change your return to the following:

return CreatedAtRoute("GetDocument",doc, null);
nPcomp
  • 8,637
  • 2
  • 54
  • 49
0

you must give a name for a method like below and return

    [HttpGet("{id}",Name = "MethodName")]
    public async Task<IActionResult> MethodName(int id)
    {
        var baseItem = await _baseItemService.BaseItemByIdAsync(baseItemId);
        return Ok(baseItem);
    }

then you need return CreatedAtRoute in Post or Put Method to get Item by Id or etc such as

var (errorMessage, baseItem) = await _baseItemService.AddBaseItem(model);
if (baseItem != null) return CreatedAtRoute("MethodName", new { baseItemId = baseItem.BaseItemId }, baseItem);
return BadRequest(errorMessage );

me must give the same method name

0

I had similar issues though not with get-async. It turned out that I was using a wrong action name, the easiest work around is to ensue your using the correct action name and I advice sometimes to just copy and past it.

Pope Francis
  • 557
  • 8
  • 9
0

This error occurs due to the confusion between the method overloads for CreatedAtAction method

overload methods for CreatedAtAction

It is suffice if you could give prefix the parameter names before you pass them as input to the CreatedAtAction method as below

 return CreatedAtAction(actionName: "GetDocument", routeValues: new { id = yourDTO.Id }, value: yourDTOObject);

This helped me!