Aspnet-api-versioning: Getting System.InvalidOperationException: No route matches the supplied values. while using CreatedAtAction

Created on 1 Feb 2020  路  6Comments  路  Source: microsoft/aspnet-api-versioning

[HttpPost]
        public async Task<IActionResult> PostAsync(RequestUserDto requestUserDto) {
            if (ModelState.IsValid && requestUserDto.RoleId != Guid.Empty) {

                    MOM.Data.EntityModels.User user = _mapper.Map<MOM.Data.EntityModels.User>(requestUserDto);
                    await _userService.CreateAsync(user);
                    return CreatedAtAction("GetAsync", "User", new { Id = user?.Id }, "text");
            }
            return BadRequest(ModelState);
        }
[HttpGet("{id:Guid}", Name = nameof(GetAsync))]//Space in id:Guid(like "id : Guid" instead of id:Guid) results in error
        public async Task<IActionResult> GetAsync(Guid id) {
            if (ModelState.IsValid) {
                var user = await _userService.GetByIdAsync(id);
                if (user != null) {
                    return Ok(_mapper.Map<ResponsePostUserDto>(user));
                }
                return NoContent();
            }
            return BadRequest(ModelState);
        }



md5-111bdd4a352fa9f96a7811f9bdbcc62c



> System.InvalidOperationException: No route matches the supplied values.
>    at Microsoft.AspNetCore.Mvc.CreatedAtActionResult.OnFormatting(ActionContext context)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncCore(ActionContext context, ObjectResult result, Type objectType, Object value)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsync(ActionContext context, ObjectResult result)
>    at Microsoft.AspNetCore.Mvc.ObjectResult.ExecuteResultAsync(ActionContext context)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultAsync(IActionResult result)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeNextResultFilterAsync[TFilter,TFilterAsync]()
> --- End of stack trace from previous location where exception was thrown ---
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
> --- End of stack trace from previous location where exception was thrown ---
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
>    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
>    at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
>    at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()



md5-af7cc5675b0f6377ebfe6ce12144010f



return CreatedAtAction("GetAsync", "User", new { Id = user?.Id }, "text");
return CreatedAtAction("GetAsync", new { Id = user?.Id }, "text");
return CreatedAtAction(nameof(GetAsync), new { Id = user?.Id }, "text");
return CreatedAtAction(nameof(GetAsync), new { user?.Id }, "text");

Now I am not sure what I am doing wrong here.

answered asp.net core external question

Most helpful comment

Thanks for the response. I was trying few scenarios on this and found that if ActionName attribute is used like this
[ActionName("GetAsync")]
it works properly.
May be you can take this as input.
Below is my working code :

[ActionName("GetAsync")]/*This required for CreatedAtAction in Post method otherwise has to version the API method*/
[HttpGet("GetAsync/{id}")]
public async Task<IActionResult> GetAsync(Guid id) {
   if (ModelState.IsValid) {
     var user = await _userService.GetByIdAsync(id);
     if (user != null) {
      return Ok(_mapper.Map<ResponsePostUserDto>(user));
      }
     return NoContent();
 }
 return BadRequest(ModelState);
 }
[HttpPost]
public async Task<IActionResult> PostAsync(RequestUserDto requestUserDto) {
 if (ModelState.IsValid && requestUserDto.RoleId != Guid.Empty) {
  MOM.Data.EntityModels.User user = _mapper.Map<MOM.Data.EntityModels.User>(requestUserDto);
   await _userService.CreateAsync(user);
  return CreatedAtAction(Common.HttpMethods.GetAsync.ToString(), new { user?.Id }, _mapper.Map<ResponsePostUserDto>(user));
 }
  return BadRequest(ModelState);
}



md5-d7c56081ba1c6d0790893a91e185aa1d



 [ActionName("GetAsync")]
        [Route("item/{id}")]
        [HttpGet("GetAsync")]
        public async Task<IActionResult> GetAsync(Guid id) {
            if (ModelState.IsValid) {
                var user = await _userService.GetByIdAsync(id);
                if (user != null) {
                    return Ok(_mapper.Map<ResponsePostUserDto>(user));
                }
                return NoContent();
            }
            return BadRequest(ModelState);
        }

Can you please verify whether this the expected behavior?

All 6 comments

558 I found this :

Prefer CreatedAtAction over CreatedAtRoute as it does not have the naming restrictions. This obviously isn't an option for link generation outside of the current controller.

But it doesn't seem to work in my case.

If do version my Function name like GetAsyncV1 and use nameof(GetAsyncV1) then it's working properly. But I want my method name to be

GetAsync()

While using CreatedAtAction.
How can I do it?

Based on the information you've provided, this is almost certainly because of the Async suffix in your action names. I've seen people get hung up by this before. This is completely out of my hands and is a change in ASP.NET Core itself that addresses a separate bug. This behavior is called out as a breaking change in release notes here. There is apparently a way to enable the old behavior should you want to keep things they way they were.

I hope that helps.

Thanks for the response. I was trying few scenarios on this and found that if ActionName attribute is used like this
[ActionName("GetAsync")]
it works properly.
May be you can take this as input.
Below is my working code :

[ActionName("GetAsync")]/*This required for CreatedAtAction in Post method otherwise has to version the API method*/
[HttpGet("GetAsync/{id}")]
public async Task<IActionResult> GetAsync(Guid id) {
   if (ModelState.IsValid) {
     var user = await _userService.GetByIdAsync(id);
     if (user != null) {
      return Ok(_mapper.Map<ResponsePostUserDto>(user));
      }
     return NoContent();
 }
 return BadRequest(ModelState);
 }
[HttpPost]
public async Task<IActionResult> PostAsync(RequestUserDto requestUserDto) {
 if (ModelState.IsValid && requestUserDto.RoleId != Guid.Empty) {
  MOM.Data.EntityModels.User user = _mapper.Map<MOM.Data.EntityModels.User>(requestUserDto);
   await _userService.CreateAsync(user);
  return CreatedAtAction(Common.HttpMethods.GetAsync.ToString(), new { user?.Id }, _mapper.Map<ResponsePostUserDto>(user));
 }
  return BadRequest(ModelState);
}



md5-d7c56081ba1c6d0790893a91e185aa1d



 [ActionName("GetAsync")]
        [Route("item/{id}")]
        [HttpGet("GetAsync")]
        public async Task<IActionResult> GetAsync(Guid id) {
            if (ModelState.IsValid) {
                var user = await _userService.GetByIdAsync(id);
                if (user != null) {
                    return Ok(_mapper.Map<ResponsePostUserDto>(user));
                }
                return NoContent();
            }
            return BadRequest(ModelState);
        }

Can you please verify whether this the expected behavior?

One more thing if you can confirm
[HttpGet("{id:int}", Name = nameof(GetAsync))] this code works fine
[HttpGet("{id : int}", Name = nameof(GetTestByIdv1))] but this throws error(space in "id : int")
Is this the expected behavior or it can be fixed to work with spaces?

Your question is more general about how routing works in ASP.NET Core, which is better suited on Stackoverflow or the ASP.NET repo; however, your setup seems like should be fine. Route templates have their own markup and parsing rules. To my knowledge, white space is not allowed inside route constraints. I hope that helps.

It would seem that this issue has been resolved. Yes?

This issue has been idle for quite some time. The original question appears to have been answered and resolved. If there are more question specific to API Versioning, I'm happy to answer them. Thanks.

Was this page helpful?
0 / 5 - 0 ratings