为什么需要构建Web服务层?
目前我们用Spring Boot构建的应用可以访问数据库,进行数据的存储和提取。但是我们的应用还没有对外提供服务的能力,在当下的分布式系统及微服务架构中,RESTful风格是一种主流的 Web 服务表现方式。接下来重点讨论下对RESTful的理解
RESTful的API你可能经常听到,那具体是什么含义,什么样的API才满足RESTful的API呢,可能就比较迷惑了,
REST(Representational State Transfer,表述性状态转移)表述性状态转移这个确实抽象,它本质上只是一种架构风格而不是一种规范,这种架构风格把位于服务器端的访问入口看作一个资源,每个资源都使用 URI(Universal Resource Identifier,统一资源标识符) 得到一个唯一的地址,且在传输协议上使用标准的 HTTP 方法,比如最常见的 GET、PUT、POST 和 DELETE。满足这样风格的API,我们称之为RESTful API。举个例子,说明下满足RESTful风格API如何定义:
URL | HTTP请求方法 | 描述 |
http://www/example.com/v1/users | GET | 获取User对象列表 |
http://www/example.com/v1/user/{id} | GET | 根据id查询User对象 |
http://www/example.com/v1/user | POST | 新增User对象 |
http://www/example.com/v1/user | PUT | 更新User对象 |
http://www/example.com/v1/user/{id} | DELETE | 根据ID删除User对象 |
另一方面,客户端与服务器端的数据交互涉及序列化、反序列号问题。关于序列化完成业务对象在网络环境上的传输的实现方式有很多,常见的有文本和二进制两大类。
目前 JSON 是一种被广泛采用的序列化方式,本课程中所有的代码实例我们都将 JSON 作为默认的序列化方式。
说了对RESTful理解后,使用Spring Boot如何构建RESTful风格的Web服务我们来个简单入门。
实例代码对应的仓库地址:
github.com/dragon8844/…
gitee.com/drag0n/spri…
在pom.xml的文件中引入相关依赖:
org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.jupiter junit-jupiter-api junit junit test 复制代码
在resources目录下创建应用的配置文件application.yml,添加如下配置内容:
# 用来修改web服务的端口server: port: 8080复制代码
配置文件中配置web服务的端口,如果不配置,默认使用也是8080端口。
定义User请求VO
@Datapublic class UserReqVO implements Serializable { private String username; private String password;}复制代码
定义User响应VO
@Datapublic class UserRespVO implements Serializable { private Integer id; private String username; private Date createTime;}复制代码
编写Controller用来暴露API
@RestController@RequestMapping(value = "v1")@Slf4jpublic class UserController { @PostMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE) public R insert(@RequestBody UserReqVO userReqVO){ log.info("接受的参数,username:{}, password:{}", userReqVO.getUsername(), userReqVO.getPassword()); //调用Service层保存user return R.ok(true); } @GetMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE) public R selectById(@PathVariable Integer id){ log.info("接受的参数id:{}",id); //模拟生成UserRespVO对象,实际应该是调用Service获取 UserRespVO userRespVO = new UserRespVO(); userRespVO.setCreateTime(new Date()); userRespVO.setId(id); userRespVO.setUsername("张三"); return R.ok(userRespVO); } @PutMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE) public R updateById(@PathVariable Integer id, @RequestBody UserReqVO userReqVO){ log.info("接受的参数,username:{}, password:{}",userReqVO.getUsername(), userReqVO.getPassword()); //调用Service层更新user return R.ok(true); } @DeleteMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE) public R deleteById(@PathVariable Integer id){ log.info("接受的参数id:{}",id); //调用Service层删除user return R.ok(true); }}复制代码
在Controller中我们针对User资源定义了4个API,分别对应User资源新增、根据id查询User、根据id更新User、根据id删除User。我们使用了一系列的注解@RestController、@RequestMapping、@PostMapping、@PutMapping、@GetMapping、@DeleteMapping、@RequestBody、@PathVariable等等,稍后会具体介绍各个注解的含义。
到这里,一个典型的 RESTful 服务已经开发完成了,现在我们可以通过 java –jar 命令直接运行 Spring Boot 应用程序了。那么如何验证我们写的API是否可用,我们将引入 Postman 来演示如何通过 HTTP 协议暴露的API进行远程服务访问。我们以查询User为例,使用Postman 访问“http://localhost:8080/v1/user/1”端点以得到响应结果。
除了使用Postman第三方的工具进行测试,我们还可以编写针对Controller层的单元测试
@RunWith(SpringRunner.class)@SpringBootTest@AutoConfigureMockMvc@Slf4jpublic class UserControllerTest { @Autowired private MockMvc mockMvc; @Test public void insert() throws Exception { UserReqVO userReqVO = new UserReqVO(); userReqVO.setPassword("123456"); userReqVO.setUsername("张三"); //设置值 ObjectMapper mapper = new ObjectMapper(); ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter(); String requestJson = ow.writeValueAsString(userReqVO); MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/v1/user") .contentType(MediaType.APPLICATION_JSON) .content(requestJson)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn(); log.info("mvcResult:{}", mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8"))); } @Test public void selectById() throws Exception { MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/v1/user/1")) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn(); log.info("mvcResult:{}", mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8"))); } @Test public void updateById() throws Exception{ UserReqVO userReqVO = new UserReqVO(); userReqVO.setPassword("123456"); userReqVO.setUsername("张三"); //设置值 ObjectMapper mapper = new ObjectMapper(); ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter(); String requestJson = ow.writeValueAsString(userReqVO); MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.put("/v1/user/1") .contentType(MediaType.APPLICATION_JSON) .content(requestJson)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn(); log.info("mvcResult:{}", mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8"))); } @Test public void deleteById()throws Exception{ MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.delete("/v1/user/1")) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn(); log.info("mvcResult:{}", mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8"))); }}复制代码
在快速入门中,我们使用Spring Boot来暴露我们Web服务的时候,使用了很多注解,接下来我将会详细介绍下每个注解。
在原有 Spring Boot 应用程序的基础上,我们可以通过构建一系列的 Controller 类暴露 RESTful 风格的 API。这里的 Controller 与 Spring MVC 中的 Controller 概念上一致,最简单的 Controller 类如下代码所示:
@RestControllerpublic class HelloController { @GetMapping("/") public String index() { return "Hello World!"; }}复制代码
上述代码使用了@RestController和@GetMapper这两个注解。
@RestController 注解继承自 Spring MVC 中的 @Controller 注解,顾名思义就是一个基于 RESTful 风格的 HTTP 端点,并且会自动使用 JSON 实现 HTTP 请求和响应的序列化/反序列化方式。
通过这个特性,在构建 RESTful 服务时,我们可以使用 @RestController 注解代替 @Controller 注解以简化开发。不必没有方法上添加@ResponseBody注解。
另外一个 @GetMapping 注解也与 Spring MVC 中的 @RequestMapping 注解类似。我们先来看看 @RequestMapping 注解的定义,该注解所提供的属性都比较容易理解,如下代码所示:
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface RequestMapping { String name() default ""; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {};}复制代码
@GetMapping 的注解的定义与 @RequestMapping 非常类似,只是默认使用了 RequestMethod.GET 指定 HTTP 方法,如下代码所示:
@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@RequestMapping( method = {RequestMethod.GET})public @interface GetMapping {}复制代码
除了@GetMapping注解外,Spring Boot 2.X还提供了 @PostMapping、@PutMapping、@DeleteMapping、@PatchMapping,这些注解都是显示的指定了Http请求的方式,方便开发了开发人员的使用。当然也可以使用@RequestMapping注解达到相同的效果。
Spring Boot 提供了一系列简单有用的注解来简化对请求输入的控制过程,常用的包括 @PathVariable、@RequestParam、@RequestBody和@ModelAttribute注解。
其中 @PathVariable 注解用于获取路径参数,即从类似 url/{id} 这种形式的路径中获取 {id} 参数的值。
该注解定义如下:
@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface PathVariable { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; boolean required() default true;}复制代码
使用 @PathVariable 注解时,我们只需要指定一个参数的名称和URL中的参数值进行绑定,如下实例代码:
@GetMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE) public R selectById(@PathVariable Integer id){复制代码
{id}和方法上的参数id绑定。
@RequestParam注解的作用与@PathVariable 注解类似,也是用于获取请求中的参数,但是它面向类似 url?id=XXX 这种路径形式。
该注解的定义如下代码所示,相较 @PathVariable 注解,它只是多了一个设置默认值的 defaultValue 属性。
@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestParam { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; boolean required() default true; String defaultValue() default ValueConstants.DEFAULT_NONE;}复制代码
@RequestBody 注解用来处理 content-type 为 application/json 类型时的编码内容,通过 @RequestBody 注解可以将请求体中的 JSON 字符串绑定到相应的 JavaBean 上。
如下代码所示就是一个使用 @RequestBody 注解来控制输入的场景。
@PostMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE) public R insert(@RequestBody UserReqVO userReqVO){复制代码
如何通过Spring Boot构建RESTful Web服务的内容就这么多,我们首先介绍对RESUful理解,然后通过一个简单的入门讲解了如何通过Spring Boot来构建一个标准的RESTful API。并且在入门教程中,我们体验到了Spring Boot提供的强大的注解,帮我们很简单就实现了该功能。后面我们对Spring Boot提供的注解进行了总结汇总,详细介绍了每个注解的具体功能和使用方式。
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。
留言与评论(共有 0 条评论) “” |