.Net WebApi 命名规范

大象医生公司HTTP Service开发指南

1 WebAPI接口实现规范

1.1 GET方法的几种实现状况

1.1.1 基本实现

// GET: api/v1/Products/1455ebe0-832b-46c5-8772-b9483d947a63

    /// <summary>
    /// 获取特定产品
    /// </summary>
    /// <param name="id">产品标识</param>
    /// <returns></returns>
    [Route("~/api/v1/Products/{id}")]
    [ResponseType(typeof(ProductOutput))]
    public IHttpActionResult GetProduct(Guid id)
    {
        var result = productService.GetProductById(id);
        return Ok(result);
    }

规范编程

  • 首行:以一个具体的URL示例做为首行,URL以HTTP方法名开始,HTTP方法名大写,后跟冒号+空格+URL。要求在运行时使用该URL进行调试可以进入方法体;
  • 注释行:建议以获取XX资源或符合XX条件的XX资源做为描述,例如:获取正在促销的产品;
  • 路由:在符合默认路由规则时,该部分是可选的。参数部分,做为本资源的标识时,不须要添加资源名称做为前缀。例如,这里不使用productId,而直接使用id。仅当URL中出现其它资源标识时,该其它资源标识须要添加用于限定的资源名称做为前缀。Route特性老是出如今接口方法或ResponseType特性之上。
  • 响应类型:当接口方法返回的是IHttpActionResult类型时,Swagger没法推导具体的响应类型,必须使用ResponseType加以声明,以便生成正确的Swagger元数据。ResponseType特性老是出如今接口方法(或SwaggerResponse特性)之上。
  • 接口方法声明:方法命名老是使用完整的语义,以便清晰描述本方法的具体功能。Swagger根据方法名生成元数据中的operationId,且operationId不容许重复。因此必须确保语义表述准确以免重复。然而在本例中,使用GetProduct或GetProducts来返回特定id的产品或所有产品是容许的命名惯例,并不须要特地命名为GetProductById或GetAllProducts。
  • 返回结果中间变量:建议定义中间变量var result获取返回结果值,以便于调试,并使最终结果返回代码return Ok()中的参数显得更简洁。

1.1.2 带有多个状态码返回的实现

// GET: api/v1/Products/1455ebe0-832b-46c5-8772-b9483d947a63

    /// <summary>
    /// 获取特定产品
    /// </summary>
    /// <param name="id">产品标识</param>
    /// <returns></returns>
    [Route("~/api/v1/Products/{id}")]
    [ResponseType(typeof(ProductOuput))]
    [SwaggerResponse(HttpStatusCode.NotFound)]
    public IHttpActionResult GetProduct(Guid id)
    {
        var result = productService.GetProductById(id);
        if (result == null)
        {
            return NotFound();
        }

        return Ok(result);
    }

规范api

  • SwaggerResponse:SwaggerResponse特性用于描述接口返回除200之外的其它状态码,这将在元数据中生成对应的描述。建议SwaggerResponse老是出现接口方法之上。入参包括StatusCode, Description和Type。当没法利用StatusCode推断返回意义时,必须使用Description注明描述;当应答体有返回内容时,必须使用Type标注返回类型。
  • 使用XML Documentation的response配置节也能够达到与SwaggerResponse相似的效果,以下所示。须要注意的是,二者出现冲突时,SwaggerResponse的优先级更高。ui

    /// <response code="404">Not Found</response>

1.1.3 返回集合结果的实现

// GET: api/v1/Products

    /// <summary>
    /// 获取全部产品
    /// </summary>
    /// <returns>产品集合</returns>
    [Route("~/api/v1/Products")]
    public IEnumerable<ProductOutput> GetProducts()
    {
        var result = productService.GetProducts();
        return result;
    }

规范3d

  • 当返回集合做为结果时,若是结果集为空,不须要返回404,只需返回空结果集。所以,在不须要有其它非200返回的状况下,方法的返回类型能够是集合类型,而非IHttpActionResult。

1.1.4 返回分页集合结果的实现

// GET: api/v1/Products?Pager.PageIndex=1&Pager.PageSize=10

    /// <summary>
    /// 获取全部产品的分页列表
    /// </summary>
    /// <returns>产品分页列表</returns>
    [Route("~/api/v1/Products")]
    public IPagedList<ProductOutput> GetPagedProducts([FromUri] Pager pager)
    {
        var result = productService.GetPagedProducts(pager);
        return result;
    }

规范调试

  • 分页器、筛选器、排序器等,应该经过Query String传递。本例中,Query String中的参数PageIndex和PageSize必须使用Pager前缀,才能绑定至方法参数pager中。
  • Pager, IPagedList, SortBy等类型的实现,由Elephant.Core核心库提供。关于如何引用企业核心库可参考《大象医生公司核心库开发与发布说明》。

1.2 POST方法的几种实现状况

1.2.1 建立资源的基本实现

// POST: api/v1/Products

    /// <summary>
    /// 建立产品
    /// </summary>
    /// <param name="product">产品</param>
    /// <returns></returns>
    [Route("~/api/v1/Products")]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.BadRequest)]
    [SwaggerResponse(HttpStatusCode.Created, Type = typeof(ProductOutput))]
    public IHttpActionResult PostProduct([FromBody]ProductInput product)
    {
        if (ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var result = productService.CreateProduct(product);

        return CreatedAtRoute("DefaultApi", new { id = product.Id }, result);
    }

规范日志

  • 建立资源成功时,应当返回201 Created而不是200 OK。同时,须要在HTTP response headers的location节中返回所建立的新资源对应的URL,同时返回该资源的内容。
  • SwaggerResponseRemoveDefaults:能够在生成的元数据中取消默认返回状态码200。此例中,201将成为新的默认返回状态码,显示在Swagger文档中。
  • 当有DTO做为输入时,须要对DTO进行模型合法性校验。使用ModelState.IsValid进行校验,并在校验失败时,返回400,同时返回ModelState。ModelState包含了模型校验失败的具体缘由。
  • 使用CreatedAtRoute能够应用一个现成的路由。本例中使用了名称为DefaultApi的默认路由,此默认路由一般定义在WebApiConfig.cs文件中。但更多的状况是,使用一个已存在的自定义路由,此时须要将这一路由声明为一个具名路由。例如,前例中GET方法的路由,能够改写为以下方式,使之成为一个具名路由,并使用GetProductById这一名称(替换本例中的DefaultApi),此路由即用于建立新资源的URL。本例中new { id = product.Id }中的参数id,将会替换该路由中的参数id,而result则做为Content中的内容返回。code

    [Route("~/api/v1/Products/{id}", Name = "GetProductById")]
  • 本例中,方法的入参product应当为一个做为Input的DTO,而result变量应当为一个做为Output的DTO。相关的DTO的命名以Input或Output做为后缀。后缀不并仅限于使用Input和Output,也可使用Create或Update等,进一步区分用途。排序

1.2.2 其它非幂等性操做的实现

// POST: api/v1/Products/Last/Remove

    /// <summary>
    /// 删除最后一个产品
    /// </summary>
    /// <returns></returns>
    [Route("~/api/v1/Products/Last/Remove")]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.NotFound)]
    [SwaggerResponse(HttpStatusCode.NoContent)]
    public IHttpActionResult RemoveLastProduct()
    {
        var result = productService.RemoveLastProduct();
        if (!result)
        {
            return NotFound();
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

规范接口

  • 本例中,删除最后一个产品的操做知足非幂等性,即屡次操做可能删除多个不一样的产品,使得产生不一样的系统状态。对于原本在语义上有可能使用PUT或DELETE的操做,若是其知足非幂等性,都应当使用POST方法。
  • 当方法表明的操做再也不用于建立资源时,使用操做名词自己替代Post用于方法的命名,所以本例中的方法不是PostRemoveLastProduct。
  • 当使用非Post前缀的方法名称时,按照命名惯例,Web API将默认该方法为Post方法。所以本例中不须要加HttpPost特性(其它HTTP方法须要显式声明)。
  • 当操做不包含返回值时,应返回204 NoContent。

1.3 PUT方法的几种实现状况

1.3.1 更新资源的基本实现

// PUT: api/v1/Products/1455ebe0-832b-46c5-8772-b9483d947a63

    /// <summary>
    /// 更新产品
    /// </summary>
    /// <param name="id">产品标识</param>
    /// <param name="product">产品</param>
    /// <returns></returns>
    [Route("~/api/v1/Products/{id}")]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.NotFound)]
    [SwaggerResponse(HttpStatusCode.NoContent)]
    public IHttpActionResult PutProduct(Guid id, [FromBody]ProductInput product)
    {
        if (ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var result = productService.GetProductById(id);
        if (result == null)
        {
            return NotFound();
        }

        productService.UpdateProduct(id, product);

        return StatusCode(HttpStatusCode.NoContent);
    }

规范ip

  • 根据官方的定义,使用PUT方法,意味着或者不存在该资源,则建立一个新的资源;或者存在该资源,使用新的资源完整替换原有资源。不管哪种,屡次操做后获得的系统状态的结果是彻底一致的,于是符合幂等性。在本例中,并无由于不存在该资源而建立新的资源,而是返回了404 NotFound,但仍然没有违反幂等性。具体采用哪种策略,应该根据实际应用场景须要决定。若是建立新资源,则应当返回201 Created。
  • 资源的标识在方法中,经过参数id独立传递,ProductInput并不包含id属性。在更新前,须要判断id对应的资源是否存在,而后进行进一步的操做。

1.3.2 其它幂等性操做的实现

// PUT: api/v1/Products/1455ebe0-832b-46c5-8772-b9483d947a63/Disable

    /// <summary>
    /// 下架产品
    /// </summary>
    /// <param name="id">产品标识</param>
    /// <returns></returns>
    [HttpPut]
    [Route("~/api/v1/Products/{id}/Disable")]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.NotFound)]
    [SwaggerResponse(HttpStatusCode.NoContent)]
    public IHttpActionResult DisableProduct(Guid id)
    {
        var result = productService.GetProductById(id);
        if (result == null)
        {
            return NotFound();
        }

        productService.DisableProduct(id);

        return StatusCode(HttpStatusCode.NoContent);
    }

规范

  • 当方法表明的操做再也不用于更新资源时,使用操做名词自己替代Put用于方法的命名。因为命名惯例决定了默认HTTP方法是POST,所以这里须要显式标识HttpPut特性。
  • 因为屡次下架同一产品的结果是一致的,因此本例的操做符合幂等性。

1.4 DELETE方法的几种实现状况

1.4.1 删除资源的基本实现

// DELETE: api/v1/Products/1455ebe0-832b-46c5-8772-b9483d947a63

    /// <summary>
    /// 删除产品
    /// </summary>
    /// <param name="id">产品标识</param>
    /// <returns></returns>
    [Route("~/api/v1/Products/{id}")]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.NotFound)]
    [SwaggerResponse(HttpStatusCode.NoContent)]
    public IHttpActionResult DeleteProduct(Guid id)
    {
        var result = productService.GetProductById(id);
        if (result == null)
        {
            return NotFound();
        }

        productService.DeleteProduct(id);

        return StatusCode(HttpStatusCode.NoContent);
    }

规范

  • DELETE方法应当知足幂等性。本例中,屡次删除操做都将使系统状态改变为产品已删除的状态,所以虽然状态码的返回存在多个可能,但系统状态始终是一致的。

1.5 幂等性的界定

幂等性应关注发送多个重复的操做,系统状态的结果是否始终一致,而不是关注接口返回是否一致。系统状态在这里主要指的是业务状态,而不包括那些业务以外额外生成的状态变动,例如日志、统计数据等。符合幂等性的写操做,可使用POST、DELETE方法;不然,即便语义上属于更新或删除操做,也应当使用POST方法,以符合HTTP协议规定,确保基于协议之上的一些外部行为的结果是符合预期的。

1.5 状态返回码

  • 上述示例中,操做成功后没有返回内容的,200 OK和204 NoContent在大多数状况下都是通用的。但为了编程一致上的考虑,统一使用204 NoContent返回。
  • 当违反业务规则约束使得操做失败时,应当返回409 Conflict。
相关文章
相关标签/搜索