在 .NET Core 中构建 REST API (4)

在 Program 类中,启用 NLog,记得添加 using NLog.Web:

Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .UseNLog();

最后,在 appsettings.json 中进行以下微调来配置日志记录:

"Logging": { "LogLevel": { "Default": "Information", "Microsoft": "None", "Microsoft.AspNetCore": "Error", "Microsoft.Hosting.Lifetime": "Information" } }

这里的基本思想是减少与此 API 无关的日志条目的数量。您可以随意调整这些设置,以便恰当地记录 API 所需要的日志内容。

是时候言归正传了,在 Controller 类中,添加 using Microsoft.Extensions.Logging 并注入一个普通的旧式 ASP.NET logger:

private readonly ILogger<ProductsController> _logger; public ProductsController(ProductContext context, ILogger<ProductsController> logger) { _logger = logger; // ... }

假设,现在您的团队决定要抓取客户端请求获取 100 条或更多条记录的频率相关的遥测数据。

将下面的代码放入 GetProducts 中:

if (request.Limit >= 100) _logger.LogInformation("Requesting more than 100 products.");

请确保有一个已存在的临时文件夹来核查日志,例如:C:\temp\BuildRestApiNetCore\。

一条日志记录看起来可能是这样的:

{ "Timestamp": "2020-07-12 10:30:30.8960", "Level": "INFO", "Logger": "BuildRestApiNetCore.Controllers.ProductsController", "Action": "GetProducts", "Message": "Requesting more than 100 products." } 带动词的 REST Endpoints

深吸一口气,然后畅快地呼出。该 API 差不多可以投入生产环境了,而且只用了很少的代码。现在,我将快速转向 POST、PUT、PATCH 和 DELETE 等 REST 特性的介绍。

POST Endpoint 接收带有新产品的 body,并将其添加到列表当中。此方法是非幂等的,因为它在调用时会创建新的资源。

将下面的代码放入 ProductsController 中:

[HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult<Product> PostProduct([FromBody] Product product) { try { _context.Products.Add(product); _context.SaveChanges(); return new CreatedResult($"/products/{product.ProductNumber.ToLower()}", product); } catch (Exception e) { _logger.LogWarning(e, "Unable to POST product."); return ValidationProblem(e.Message); } }

ASP.NET 通过 ValidationProblem 自动处理异常。该验证将返回一条符合 RFC 7807 规范的响应,并带有一条消息。在实际的系统中,我建议确保不要暴露任何关于 API 的内部信息。将异常信息放在此处有助于客户端对代码进行故障排除,但安全性也很重要。我在这里选择包含错误信息主要是为了演示目的。此处还会将异常记录为警告,以避免记录大量的错误。当异常太多时,监控工具可能会呼叫值班人员。最佳实践是仅在可能需要人工干预的灾难性故障期间记录错误。

使用 swagger 工具,curl 命令为:

curl -i -X POST :5000/v1/products -H "accept: application/json" -H "Content-Type: application/json" -d "{\"productNumber\":\"string\",\"name\":\"string\",\"price\":10,\"department\":\"string\"}"

当请求有问题时,API 会如下响应:

{ "errors": {}, "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title":"One or more validation errors occurred.", "status": 400, "detail": "An item with the same key has already been added. Key: string", "traceId":"|c445a403-43564e0626f9af50." }

400 (Bad Request) 响应表示请求中的用户错误。因为无法信任用户发送有效数据,所以 API 会记录一个警告。

请注意,如果成功,POST 将返回带有 Location 的 201:

HTTP/1.1 201 Created Date: Mon, 13 Jul 2020 22:52:46 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Content-Length: 76 Location: /products/bc916 api-supported-versions: 1.0

这将引导客户端转向新资源。此处,转向 GET Endpoint 是个好主意:

[HttpGet] [Route("{productNumber}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult<Product> GetProductByProductNumber([FromRoute] string productNumber) { var productDb = _context.Products .FirstOrDefault(p => p.ProductNumber.Equals(productNumber, StringComparison.InvariantCultureIgnoreCase)); if (productDb == null) return NotFound(); return Ok(productDb); }

404 响应表示该资源在 API 中尚不存在,但可能会在将来的某个时候变得可用。

PUT 是类似的:

[HttpPut] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult<Product> PutProduct([FromBody] Product product) { try { var productDb = _context.Products .FirstOrDefault(p => p.ProductNumber.Equals(product.ProductNumber, StringComparison.InvariantCultureIgnoreCase)); if (productDb == null) return NotFound(); productDb.Name = product.Name; productDb.Price = product.Price; productDb.Department = product.Department; _context.SaveChanges(); return Ok(product); } catch (Exception e) { _logger.LogWarning(e, "Unable to PUT product."); return ValidationProblem(e.Message); } }

在 REST 设计中,PUT 允许对整个资源进行更新。它是幂等的,因为多次相同的请求不会改变资源的数量。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxjsd.html