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

时间:2017-12-22 10:30:04

标签: asp.net asp.net-core

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

这是我尝试过的:

我有一个获取资源,我想指出它。它需要一个ID作为输入:

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

当通过我的 POST 路线创建产品时,我想通过位置标题指向此资源:

尝试1

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

这会返回 {Location>标题 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);
    }

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

引用RFC 7231

201(已创建)状态码表示请求已被执行 实现并导致创建了一个或多个新资源。 由请求创建的主要资源由 响应中的“位置标题”字段;如果没有,则为“位置”字段 有效请求URI接收到。

什么标识资源取决于上下文。在我的解释中,如果创建的资源位于<request_uri>/<id>,则标识符可以仅为<id>

答案 2 :(得分:1)

我认为

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);
    }
}

发出以下请求:

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" }

将产生以下响应:

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"}