[TOC]
1、紧密耦合
避免在控制器中声明特定的依赖项实例,应该使用依赖注入系统将依赖项注入到控制器中。后者避免了紧密耦合,更加易于维护和测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| [ApiController] [Route("[controller]")] public class ProductController : ControllerBase { ProductService productService = new ProductService(); }
[ApiController] [Route("[controller]")] public class ProductController : ControllerBase { private readonly ILogger<ProductController> _logger; private readonly IProductService _productService;
public ProductController(IProductService productService, ILogger<ProductController> logger) { _logger = logger; _productService = productService; } }
|
2、Mixing Concerns功能混淆
控制器应专注于 HTTP 请求和生成响应。避免混合身份验证、授权或任何其他验证等问题,而是使用中间件或任何单独的类或服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| [HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { return Ok(StatusCodes.Status201Created); }
[HttpPost] [Authorize] public async Task<IActionResult> CreateProduct(ProductDto productDto) { await _productService.CreateAsync(productDto);
return Ok(StatusCodes.Status201Created); }
|
3、缺乏异常处理
避免在控制器中使用 try-catch 块,而是使用异常中间件来更好地处理异常以返回一般错误消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| [HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { try { await _productService.CreateAsync(productDto);
return Ok(StatusCodes.Status201Created); } catch (ProductValidationException ex) { return BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "An error occurred while creating the product."); return StatusCode(StatusCodes.Status500InternalServerError); } }
[HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { await _productService.CreateAsync(productDto);
return Ok(StatusCodes.Status201Created); }
|
4、长时操作
避免在控制器中执行长时间运行的操作。相反,应该把长时操作加入到后台运行的队列中,以避免系统停止服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [HttpGet] public async Task<IActionResult> GenerateReport(Report report) {
return Ok(report); }
[HttpPost] public async Task<IActionResult> GenerateReport(Report report) { var taskIdentifier = await _messageQueueService.EnqueueAsync(report);
return StatusCode(StatusCodes.Status202Accepted, taskIdentifier); }
|
5、缺少验证
输入验证对于确保系统的完整性和安全性至关重要。避免忽视控制器中的输入验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { await _productService.CreateAsync(productDto); return Ok(StatusCodes.Status201Created); }
[HttpPost] public async Task<IActionResult> CreateProduct([FromBody] ProductDto productDto) { if (!ModelState.IsValid) { return StatusCode(StatusCodes.Status400BadRequest, ModelState); }
await _productService.CreateAsync(productDto); return Ok(StatusCodes.Status201Created); }
|
6、直接访问数据库
避免直接访问数据库,而是使用服务或存储库将控制器与特定的数据访问技术分离。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| [HttpGet] public IActionResult GetProduct(int productId) { var product = dbContext.Products.Find(productId); return Ok(product); }
[HttpGet] public async Task<IActionResult> GetProduct(int productId) { var product = await _productService.GetByIdAsync(productId); return Ok(product); }
|
7、缺乏缓存
在适当的时候实施缓存机制。利用缓存来提高性能并减少服务器上的负载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| [HttpGet] public async Task<IActionResult> GetProducts() { var products = await _productService.GetAllAsync(); return Ok(products); }
[HttpGet] [ResponseCache(Duration = 60)] public async Task<IActionResult> GetProducts() { var products = await _productService.GetAllAsync(); return Ok(products); }
|
8、缺乏身份验证和授权
对敏感操作实施身份验证和授权。相应地保护控制器和操作方法的安全访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| [HttpPost] public async Task<IActionResult> DeleteProduct(int productId) { await _productService.DeleteAsync(productId); return StatusCode(StatusCodes.Status200OK); }
[HttpPost] [Authorize(Roles = "Admin")] public async Task<IActionResult> DeleteProduct(int productId) { await _productService.DeleteAsync(productId); return StatusCode(StatusCodes.Status200OK); }
|
9、逻辑过多
避免过多的逻辑。控制器应主要负责处理传入请求和返回响应。对于任何复杂的逻辑,应使用单独的实用程序或服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| [HttpGet] public async Task<IActionResult> GetProducts() { return Ok(products); }
[HttpGet] public async Task<IActionResult> GetProducts() { var products = await _productService.GetAllAsync(); return Ok(products); }
|
10、忽略 HTTP 动词实现RESTful 原则
ASP.NET Core 中的控制器应遵循 RESTful 架构的原则。避免使用不符合 RESTful 约定的不当 HTTP 谓词或操作。使用适当的 HTTP 动词(GET、POST、PUT、DELETE 等)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public IActionResult DeleteProduct(int productId) { }
[HttpDelete("/api/products/{id}")] public IActionResult DeleteProduct(int productId) { }
|
11、缺乏正确的路由
确保控制器已正确路由以处理传入请求。避免不一致或不明确的路由配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [HttpGet] public IActionResult Get() { }
[HttpGet("api/products")] public IActionResult Get() { }
|
12、缺少日志
日志记录是应用程序开发的一个非常关键的方面,因为它有助于在代码执行期间跟踪重要事件、条件和错误。使用中间件或操作筛选器捕获相关信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| [HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { if (someSimpleCondition) { }
await _productService.CreateAsync(productDto); return Ok(); }
[HttpPost] public async Task<IActionResult> CreateProduct(ProductDto productDto) { if (someSimpleCondition) { _logger.LogWarning("Warning: Some simple condition is met."); }
await _productService.CreateAsync(productDto); return Ok(); }
|
参考:
- 12 Bad Practices to Avoid in ASP.NET Core API Controllers