我正在尝试创建一个 .net core 5 API。我的控制器中有以下代码。
[HttpGet]
public async Task<IActionResult> GetAll()
{
return Ok(await _repository.GetAllUsersAsync());
}
// GET api/<ReelTypes>/5
[Route("GetUserByIdAsync")]
[HttpGet("{id}", Name ="GetUserByIdAsync")]
public async Task<ActionResult<AppUser>> GetUserByIdAsync(Guid id)
{
var appUser = await _repository.GetUserByIdAsync(id);
if(appUser == null)
{
return NoContent();
}
return Ok(appUser);
}
[HttpPost]
[Route("CreateAsync")]
public async Task<IActionResult> CreateAsync([FromBody] AppUser model)
{
var checkExists = await _userManager.FindByNameAsync(model.UserName);
if (checkExists != null)
{
return StatusCode(StatusCodes.Status409Conflict,
new _ResponseBody { Title = "Duplicate Record",
Body = "Record already exists" });
}
AppUser user = new AppUser()
{
Email = model.Email,
SecurityStamp = Guid.NewGuid().ToString(),
UserName = model.UserName
};
var result = await _userManager.CreateAsync(user, model.PasswordHash);
if (!result.Succeeded)
return StatusCode(StatusCodes.Status500InternalServerError,
new _ResponseBody { Title = "Not Created",
Body = "Unable to create record" });
return CreatedAtAction("GetUserByIdAsync", new { id = user.Id }, user);
}
我真的很努力遵循完整的规范,并且我知道当我使用 API 创建对象时,响应需要包含新对象的 URI 和新对象。为此,我尝试使用
CreatedAtAction
函数,但无论我做什么,我都会得到以下结果:
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 ---
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 ---
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.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
我尝试使用
GetName
作为第一个参数,但最终像现在一样对其进行了硬编码。所以我需要知道的是我是个白痴(明显的答案是肯定的)还是这个函数有问题?问题是我的GetById
有一个Guid
作为参数吗?我正在使用其中一个身份表吗?我必须使用版本控制吗?
我已经浏览了这里和其他地方关于这个问题的每一篇文章,所以我不只是发布这第一件事。如果它很明显,我很抱歉我只是错过了它。
编辑:我尝试使用
nameof
函数作为第一个参数。我还尝试用 [ActionName("GetUserByIdAsync")]
装饰目标动作。
更换
return CreatedAtAction("GetUserByIdAsync", new { id = user.Id }, user);
与
return GetUserByIdAsync(user.Id );
异常消息准确地告诉您问题是什么:
没有路线与提供的值匹配。
CreatedAtAction(string actionName, object value)
的文档:
actionName:用于生成 URL 的操作的 name。
HttpGetAttribute.Name
的文档:
获取路线名称。路线名称可用于使用特定路线生成链接,而不是依赖于基于给定的一组路线值来选择路线。
所以问题是你在用
Name
装饰控制器方法时没有指定 HttpGet
。换句话说,您需要做的就是:
// GET api/<ReelTypes>/5
[HttpGet("{id}", Name = "GetUserByIdAsync")]
public async Task<ActionResult<AppUser>> GetUserByIdAsync(Guid id)
我发现我必须为 Get Action 指定参数的数据类型,重新添加 ActionName 装饰,然后将其更改回使用 nameof。所以我的代码现在看起来像:
[HttpGet]
public async Task<IActionResult> GetAll()
{
return Ok(await _repository.GetAllUsersAsync());
}
// GET api/<ReelTypes>/5
[HttpGet("{id:Guid}", Name ="GetUserByIdAsync")]
[ActionName("GetUserByIdAsync")]
public async Task<ActionResult<AppUser>> GetUserByIdAsync([FromRoute] Guid id)
{
var appUser = await _repository.GetUserByIdAsync(id);
if(appUser == null)
{
return NoContent();
}
return Ok(appUser);
}
[HttpPost]
[Route("CreateAsync")]
public async Task<IActionResult> CreateAsync([FromBody] AppUser model)
{
var checkExists = await _userManager.FindByNameAsync(model.UserName);
if (checkExists != null)
{
return StatusCode(StatusCodes.Status409Conflict,
new _ResponseBody { Title = "Duplicate Record",
Body = "Record already exists" });
}
AppUser user = new AppUser()
{
Email = model.Email,
SecurityStamp = Guid.NewGuid().ToString(),
UserName = model.UserName
};
var result = await _userManager.CreateAsync(user, model.PasswordHash);
if (!result.Succeeded)
return StatusCode(StatusCodes.Status500InternalServerError,
new _ResponseBody { Title = "Not Created",
Body = "Unable to create record" });
return CreatedAtAction(nameof(GetUserByIdAsync), new { id = user.Id }, user);
}
所以这三个更改之一似乎解决了我的问题。我怀疑我现在已经指定了参数类型(Guid),但我也想知道装饰的顺序是否重要。我第一次添加 [ActionName] 装饰时,我将它放在 [HttpGet...] 装饰上方。
感谢所有试图帮助我的人。我希望这可以帮助其他人解决这个问题。