Skip to main content

Command Palette

Search for a command to run...

如何写得一手优雅规范的SpringBoot 接口

Published
2 min readView as Markdown
如何写得一手优雅规范的SpringBoot 接口

导语

优雅的代码赏心悦目,你的代码触目惊心。

当编写 Spring Boot 接口时,优雅和规范是至关重要的。一个良好设计的接口能够提高代码的可读性、可维护性和可扩展性,从而为整个应用程序的开发和维护带来便利。

在本文中,我们将探讨如何通过遵循最佳实践和设计原则,编写出优雅规范的 Spring Boot 你的接口也可以像企业级项目接口一般规范且优雅。


严格遵循RESTful API 设计原则

  • 清晰一致的资源命名:使用准确反映 API 管理的资源的名词(例如,/articles、/users)。
@GetMapping("/articles/{id}")
public ResponseEntity<Product> getArticleById(@PathVariable Long id) {
    // ...
}
  • 标准化 HTTP 方法:遵循 CRUD 操作的 RESTful 约定(CREATE: POST、READ: GET、UPDATE: PUT、DELETE:DELETE)。
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // ...
}
  • 有意义的状态代码:返回相应的 HTTP 状态代码,如成功 (2xx)、错误 (4xx) 或服务器问题 (5xx)。
@DeleteMapping("/articles/{id}")
public ResponseEntity<?> deleteArticle(@PathVariable Long id) {
    if (productService.deleteArticle(id)) {
        return ResponseEntity.noContent().build(); // 204 No Content
    } else {
        return ResponseEntity.notFound().build(); // 404 Not Found
    }
}

关于更多restful标准,参考https://en.wikipedia.org/wiki/REST


合理利用好 Spring Boot 注解

这里所谓得合理,不是很好定义,但本着高效、简洁、清晰得原则推荐。

  • @RestController:默认情况下,将控制器标记为返回 JSON 或其他结构化数据。

这是一个综合注解,是@Controller@ResponseBody的功能于一身,一个注解作两个注解的事情,简洁高效。

@RestController
public class HelloController {
    // .....
}
  • @RequestMapping:定义每个controller的基本路径。

这样做可以使代码更加整洁和易于维护。不需要在每个方法上都重复写基本路径部分,在类级别定义基本路径可以带来更清晰、更简洁、更易维护的代码结构,同时也有助于提高开发效率和代码质量。

@RestController
@RequestMapping("/user")
public class HelloController {
    // .....
}
  • 使用简化的请求方式注解。

在不同类型的方法上直接使用@GetMapping、@PostMapping、@PutMapping@DeleteMapping注解进行标识,而不是使用笼统的 @RequestMapping(method = RequestMethod.POST)

  • 使用@PathVariable获取请求的路径变量;
@RestController
@RequestMapping("/articles")
public class ArticleController {

    @GetMapping("/{id}")
    public ResponseEntity<Article> getArticleById(@PathVariable Long id) {
        // 根据文章的id查询文章
        Article article = articleService.findArticleById(id);

        if (article != null) {
            return ResponseEntity.ok(article);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}
  • 使用@RequestBody将请求正文内容反序列化为 Java 对象。
@RestController
@RequestMapping("/api")
public class UserController {

    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // 这里的 User 对象会从请求的 JSON 数据中反序列化得到
        userService.saveUser(user);
        return ResponseEntity.ok(user);
    }
}

关于依赖注入的使用建议

  • 使用构造函数注入方式

通过在类的构造函数中接受依赖对象作为参数来进行注入。这种方式可以确保依赖在对象创建时被注入,提高了代码的可测试性和可维护性。

@RestController
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }
    // ... other controller methods
}

针对接口的异常处理

  • @ControllerAdvice的使用
@ControllerAdvice
public class ApiExceptionHandler {
    @ExceptionHandler(ArticleNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleArticleNotFound(ArticleNotFoundException ex) {
        // ... create error response with details
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
    }
}

使用DTO代替POJO的直接使用

对于数据传输对象,建议对pojo进行dto的封装,而不是使用原实体。提高代码的可读性、可维护性和数据封装性。

public class ArticleDto {
    private Long id;
    private String title;
    private String contents;
    // more
}

接口安全的建议

  • 使用SpringSecurity等安全框架进行认证授权,包括令牌机制的使用,如JWT

  • 对接口进行常见的漏洞检查并采取防范措施,比如XSSSQL注入等。

  • 使用https进行网络通信;


关于版本控制

  • 使用路径版本控制(例如,/api/v1/articles)或基于标头的版本控制。

使用版本控制 API 来管理更改并保持与客户端的兼容性。

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping("/details")
    public ResponseEntity<String> getProductDetails(@RequestHeader("Accept-Version") String version) {
        if ("v1".equals(version)) {
            return ResponseEntity.ok("Product details for version 1");
        } else if ("v2".equals(version)) {
            return ResponseEntity.ok("Product details for version 2");
        } else {
            return ResponseEntity.badRequest().body("Unsupported version");
        }
    }
}

完备的接口测试

  • 考虑使用 MockitoJUnit 等工具对每个接口进行测试,保证接口的准确性和稳健性。

本文小结

上面虽然列举好几种编写接口的规范和建议,但这些不是一成不变的,在具体的项目,还需要根据业务和项目需求做出一些让步和改动,灵活运用这些建议,你的接口也可以很优雅。代码就是一行行蓝色的诗,而不是冰冷乏味的英文串

More from this blog

[c++]浅谈函数重载解析和不明确匹配

先简单回顾下函数重载需要借助函数中的哪些属性来作为是否是重载函数的依据: 函数的参数类型 函数的参数个数(参数长度) 但是,这只是我们定义函数重载概念中的一部分,编译器在实际的函数重载过程中,还会进行一系列的二次判定和相对繁琐的规则匹配,这, 就是本文的内容。 重载决策 对于非重载函数,也就是具有唯一名称的函数嘛,只有一个函数可能与调用匹配,可以说,这样的函数一调一个准,因为唯一,所以没有选择,因为没有选择,所以无需过多的匹配流程。所以这种情况下,调用该函数只有两种 结果: 匹配 ...

Jan 21, 20252 min read6
[c++]浅谈函数重载解析和不明确匹配

提问的智慧-转载

更新日志 2022-9-15 午时 于 杭州 在原文的基础结构上调整了文章目录结构 简单进行了一下md的格式化 修改封面配图 引 ​ 在黑客世界里,当提出一个技术问题时,你能得到怎样的回答?这取决于挖出答案的难度,同样取决于你提问的方法。本指南旨在帮助你提高发问技巧,以获取你最想要的答案。 首先你必须明白,黑客们只偏爱艰巨的任务,或者能激发他们思维的好问题。 如若不然,我们还来干吗?如果你有值得我们反复咀嚼玩味的好问题,我们自会对你感激不尽。好问题是激励,是厚礼,可以提高我们的...

Jan 19, 20252 min read2
提问的智慧-转载

邪恶的非常量全局变量

在编程过程中,避免使用全局变量是一个良好的编程实践建议。当然,这里的全局变量主要是指 非常量全局变量; 尽管在小型项目中,这一点似乎看起来人畜无害,但是在大型项目中往往会出现很多问题。 新手程序员往往比较喜欢使用大量的全局变量,因为这样使用起来方便直接,特别是当设计到不同函数的多次调用传递参数时。 若无特别说明,本文后续内容中提到的全局变量均指 非常量全局变量。 全局变量的潜在危险 到目前为止,全局变量危险的最大原因是因为他们的值可以在任何地方被任何调用的函数更改,并且程序员没有简单的方法...

Jan 12, 20252 min read4
邪恶的非常量全局变量

详解设计模式|单例的进化之路

概念 单例模式(Singleton Pattern)是设计模式中一个重要的模式之一,是确保一个类在任何情况下都绝对只有一个实例。单例模式一般会屏蔽构造器,单例对象提供一个全局访问点,属于创建型模式。 根据初始化时间的不同,可以将单例模式分为两类: 饿汉式单例 懒汉式单例 当然,除了上面的两个分类之外,处于对性能、安全等方面的考量,单例模式还演化出了各种实现版本,每一种版本的演进,都是单例的一次**进化与升级,**下面就来看看单例模式的进化之路上都经历了哪些挑战与对抗。 饿汉式单例 饿...

Jan 5, 20255 min read4
详解设计模式|单例的进化之路

SpringBoot Web开发精解

SpringMVC基础回顾 当在 Spring Boot 中引入 Web 模块时,SpringBoot 会帮我们自动配置 Web 相关的组件,其中 Spring MVC 便是最重要的部分。 组件介绍 上图是 SpringMVC 的工作原理图。先介绍一下原理图中涉及的各个组件。 DispatcherServlet:前端控制器,是整个流程的控制中心,由它调用其他组件处理用户请求。 HandlerMapping:处理器映射器,负责根据用户请求的URL找到相应的Handler处理器。 Handl...

Jan 5, 20256 min read5
SpringBoot Web开发精解

Xayla

26 posts

Simple is efficiency!