如何在asp net core api中使用Created(或CreatedAtAction/CreatedAtRoute)

Ris*_*cie 13 asp.net asp.net-core

我想告诉消费者我的api关于新创建的对象的位置.我知道有Created() CreatedAtRoute(),CreatedAtAction()但我不确定如何使用它.

这是我尝试过的:

我有一个Get资源,我想指出.它需要一个ID作为输入:

    [HttpGet("/${id}", Name = "GetProduct")]
    [ProducesResponseType(typeof(Produkt), 200)]
    public IActionResult Get([FromRoute] int id)
    {
       // some code...
        return Ok(...);
    }
Run Code Online (Sandbox Code Playgroud)

当通过我的POST路由创建产品时,我想通过Location标头指向此资源:

尝试1

    [HttpPost]
    [ProducesResponseType(typeof(Produkt), 200)]
    public IActionResult CreateNewProduct([FromBody] ProduktDtoForCreate productFromBody)
    {
        //...
        return CreatedAtRoute("GetProduct", new { id = productToCreate.Id }, productToCreate);
    }
Run Code Online (Sandbox Code Playgroud)

这将返回一个位置标头:http:// localhost:5000 / $ 15003

尝试2

    [HttpPost]
    [ProducesResponseType(typeof(Produkt), 200)]
    public IActionResult CreateNewProduct([FromBody] ProduktDtoForCreate productFromBody)
    {
        //...
        return Created(new Uri($"{Request.Path}/{productToCreate.Id}", UriKind.Relative), productToCreate);
    }
Run Code Online (Sandbox Code Playgroud)

这个工作并返回/api/v1.0/produkte/16004但似乎不应该使用当前请求指向新位置.另外我不确定这是不是很好的做法?

phi*_*ngs 14

CreatedAtAction在我看来,给出了最好的输出。以下控制器代码将满足您的需求:

[Route("api/products")]
[ApiController]
public class ProductsController : ControllerBase
{
    private readonly IProductRepository productRepository;

    public ProductsController(IProductRepository productRepository)
    {
        this.productRepository = productRepository;
    }

    [HttpPost]
    [Route("")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    public ActionResult<Product> CreateProduct(ProductCreateDto product)
    {
        if (product is null)
            return BadRequest(new ArgumentNullException());

        var entity = productRepository.CreateProduct(product);

        return CreatedAtAction(nameof(GetProduct), new { id = entity.ID }, entity);
    }

    [HttpGet]
    [Route("{id}")]
    public ActionResult<Product> GetProduct(int id)
    {
        return productRepository.GetProduct(id);
    }
}
Run Code Online (Sandbox Code Playgroud)

发出以下请求:

POST http://localhost:5000/api/products HTTP/1.1 
Host: localhost:5000
Connection: keep-alive 
Content-Length: 25 
Content-Type: application/json

{ "name": "ACME Widget" }
Run Code Online (Sandbox Code Playgroud)

将产生以下响应:

HTTP/1.1 201 Created
Date: Mon, 12 Oct 2020 09:50:00 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 29
Location: http://localhost:5000/api/products/1

{"id":1,"name":"ACME Widget"}
Run Code Online (Sandbox Code Playgroud)


Gra*_*tts 5

在 Get 方法的路线中,将前导 / 和 $ 取出(即它应该只是“{id}”)。有前导 / 在那里意味着路线将相对于应用程序的基础;取出它会使方法的路由相对于控制器的基本路径。$ 被视为路由中的文字字符,因此它出现在尝试 1 中的 Location 标头中。进行更改后,您应该会发现 CreatedAtRoute 调用按预期工作。

  • 我还要补充一点,您的 CreateNewProduct 方法的 ProducesResponseType 注释应指定 201 返回码,而不是 200。 (5认同)