《Spring Boot从入门到实战》第六章习题答案

devtools/2025/2/12 17:55:29/

6.6 本章练习

1)设计并实现完整的Restful风格的人员管理模块的Web API,版本号为V1,并配置产生Swagger接口文档。

答案:

1. 配置 Swagger 接口文档 创建一个 Swagger 配置类。

package com.example.demo.config;import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class Swagger2Config implements WebMvcConfigurer {@Beanpublic OpenAPI customOpenAPI() {return new OpenAPI().info(new Info().title("API Documentation").description("使用 Spring Boot 构建的 RESTful APIs").version("V1")).components(new Components().addSecuritySchemes("bearAuth",new SecurityScheme().name("Authorization").type(SecurityScheme.Type.HTTP).scheme("bear").bearerFormat("JWT"))).addSecurityItem(new SecurityRequirement().addList("bearAuth"));}}

2. 创建人员实体类 Person 实体类

package com.example.demo.bean;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity
public class Person {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private Integer age;private String email;public Person() {}public Person(Long id, String name, Integer age, String email) {this.id = id;this.name = name;this.age = age;this.email = email;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}


3. 创建人员控制器 接下来,创建一个控制器来处理 Web API 请求,遵循 RESTful 风格 PersonOneController

package com.example.demo.controller;import com.example.demo.bean.Person;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;@RestController
@RequestMapping("/api/v1/persons")
public class PersonOneController {List<Person> personList = new ArrayList<>();public PersonOneController() {personList.add(new Person(1L, "Alice", 12, "123@163.com"));personList.add(new Person(2L, "Bob", 12, "123@163.com"));}@Operation(summary = "Get all persons", description = "Retrieve all persons from the database.")@GetMappingpublic List<Person> getAllPersons() {return personList;}@Operation(summary = "Get person by ID", description = "Retrieve a person by their unique ID.")@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Found the person"),@ApiResponse(responseCode = "404", description = "Person not found")})@GetMapping("/{id}")public Person getPersonById(@PathVariable Long id) {int personIndex = getPersonIndexUsingPersonId(id);Person person = personList.get(personIndex);return person;}private Integer getPersonIndexUsingPersonId(Long personId){int personIndex = 0;for (Person personObj : personList) {if (personObj.getId() == personId) {personIndex = personList.indexOf(personObj);}}return personIndex;}@Operation(summary = "Create a new person", description = "Create a new person in the database.")@PostMappingpublic ResponseEntity<Person> createPerson(@RequestBody Person person) {personList.add(person);return new ResponseEntity<>(person, HttpStatus.CREATED);}@Operation(summary = "Update an existing person", description = "Update an existing person by ID.")@PutMapping("/{id}")public ResponseEntity<Person> updatePerson(@PathVariable Long id, @RequestBody Person person) {int personIndex = getPersonIndexUsingPersonId(id);personList.set(personIndex, person);return new ResponseEntity<>(person, HttpStatus.OK);}@Operation(summary = "Delete a person", description = "Delete a person by their unique ID.")@DeleteMapping("/{id}")public ResponseEntity<Void> deletePerson(@PathVariable Long id) {int personIndex = getPersonIndexUsingPersonId(id);personList.remove(id);return ResponseEntity.noContent().build();}}

4. 启动并测试 启动 Spring Boot 应用并访问 Swagger UI:

http://localhost:8080/swagger-ui.html

Swagger UI 页面会显示所有 API 的文档,您可以在该页面直接测试人员管理的增、删、改、查操作。

2)实现人员管理模块的Web API控制版本,接口新版本为V2,修改V2版本的人员新增接口,并增加人员批量删除接口。

答案:

Sprinng boot对Restful的支持非常全面,因而实现Restful API非常简单,同样对于API版本控制也有相应的实现方案:

1)创建自定义的@ApiVersion注解。

2)自定义URL匹配规则ApiVersionCondition。

3)使用RequestMappingHandlerMapping创建自定义的映射处理程序,根据Request参数匹配符合条件的处理程序。

下面通过示例程序来演示Web API如何增加版本号。

1. 创建自定义注解 创建一个自定义版本号标记注解@ApiVersion,实现代码如下:

package com.example.demo.apiversion;import java.lang.annotation.*;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {int value() default 1;
}

 在上面的示例中,创建了ApiVersion自定义注解用于API版本控制,并返回了对应的版本号。

2. 自定义URL匹配逻辑 接下来定义URL匹配逻辑,创建ApiVersionCondition类并继承RequestCondition接口,将提取请求URL中的版本号与注解上定义的版本号进行对比,以此来判断某个请求应落在哪个控制器上。实现代码如下:

package com.example.demo.apiversion;import org.springframework.web.servlet.mvc.condition.RequestCondition;import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d+).*");private int apiVersion;public ApiVersionCondition(int apiVersion) {this.apiVersion = apiVersion;}public int getApiVersion() {return apiVersion;}public void setApiVersion(int apiVersion) {this.apiVersion = apiVersion;}@Overridepublic ApiVersionCondition combine(ApiVersionCondition other) {return new ApiVersionCondition(other.getApiVersion());}@Overridepublic ApiVersionCondition getMatchingCondition(HttpServletRequest request) {Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());if(m.find()){Integer version = Integer.parseInt(m.group(1));if(version >= this.apiVersion){return this;}}return null;}@Overridepublic int compareTo(ApiVersionCondition other, HttpServletRequest request) {return other.getApiVersion() - this.apiVersion;}
}

在上面的示例中,通过ApiVersionCondition类重写RequestCondition定义的URL匹配逻辑。

当方法级别和类级别都有ApiVersion注解时,通过ApiVersionRequestCondition.combine方法将两者进行合并。最终将提取请求URL中的版本号,与注解上定义的版本号进行比对,判断URL是否符合版本要求。

3. 自定义匹配的处理程序 接下来实现自定义匹配的处理程序。先创建ApiRequestMappingHandlerMapping类,重写部分RequestMappingHandlerMapping的方法,实现自定义的匹配处理程序 示例代码如下:

package com.example.demo.apiversion;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.lang.reflect.Method;public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {private static final String VERSION_FLAG = "version";private static RequestCondition<ApiVersionCondition> createCondition(Class<?> clazz) {RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);if (classRequestMapping == null) {return null;}StringBuilder mappingUrlBuilder = new StringBuilder();if(classRequestMapping.value().length > 0){mappingUrlBuilder.append(classRequestMapping.value()[0]);}String mappingUrl = mappingUrlBuilder.toString();if(!mappingUrl.contains(VERSION_FLAG)){return null;}ApiVersion apiVersion = clazz.getAnnotation(ApiVersion.class);return apiVersion == null? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());}@Overrideprotected RequestCondition<?> getCustomMethodCondition(Method method) {return createCondition(method.getClass());}@Overrideprotected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {return createCondition(handlerType);}
}

4. 配置注册自定义的RequestMappingHandlerMapping。 重写WebMvcRegistrationsConfig类,重写getRequestMappingHandlerMapping()的方法,将之前创建的ApiRequestMappingHandlerMapping注册到系统中。

package com.example.demo.apiversion;import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;@Configuration
public class WebMvcRegisterationsConfig implements WebMvcRegistrations {@Overridepublic RequestMappingHandlerMapping getRequestMappingHandlerMapping() {return new ApiRequestMappingHandlerMapping();}
}

5. 配置实现接口。 配置完成之后,接下来编写测试的控制器(Controller),实现相关接口的测试。在Controller目录下分别创建OrderV1Controller和OrderV2Controller。示例代码如下:

OrderV1Controller;

package com.example.demo.controller;import com.example.demo.apiversion.ApiVersion;
import com.example.demo.bean.JSONResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@ApiVersion(value = 1)
@RestController
@RequestMapping("api/{version}/order")
public class OrderV1Controller {@GetMapping("/delete/{orderId}")public JSONResult deleteOrderById(@PathVariable String orderId) {System.out.println("V1删除订单成功:"+orderId);return JSONResult.ok("V1删除订单成功");}@GetMapping("/detail/{orderId}")public JSONResult queryOrderById(@PathVariable String orderId) {System.out.println("V1获取订单详情成功:"+orderId);return JSONResult.ok("V1获取订单详情成功");}}

OrderV2Controller:

package com.example.demo.controller;import com.example.demo.apiversion.ApiVersion;
import com.example.demo.bean.JSONResult;
import com.example.demo.bean.Person;
import org.springframework.web.bind.annotation.*;import java.util.List;@ApiVersion(value = 2)
@RestController
@RequestMapping("api/{version}/order")
public class OrderV2Controller {@GetMapping("/detail/{orderId}")public JSONResult queryOrderById(@PathVariable String orderId) {System.out.println("V2获取订单详情成功:"+orderId);return JSONResult.ok("V2获取订单详情成功");}@GetMapping("/list")public JSONResult list(){System.out.println("V2,新增list订单列表接口");return JSONResult.ok("V2,新增list订单列表接口");}@PostMapping("/save")public JSONResult saveOrder(@RequestBody Person person){System.out.println("V2,人员新增成功");return JSONResult.ok("V2,人员新增成功");}@DeleteMapping("/deleteBatch")public JSONResult deleteBathcOrder(@RequestBody List<Long> ids){System.out.println("V2人员批量删除成功");return JSONResult.ok("V2人员批量删除成功");}}

6. 验证测试,使用postman调用接口


http://www.ppmy.cn/devtools/132569.html

相关文章

力扣(leetcode)每日一题 3255 长度为 K 的子数组的能量值 II|滑动窗口

3255. Find the Power of K-Size Subarrays II 1.题干 You are given an array of integers nums of length n and a positive integer k. The power of an array is defined as: Its maximum element if all of its elements are consecutive and sorted in ascending ord…

H5播放器EasyPlayer.js 流媒体播放器是否支持npm(yarn) install 安装?

EasyPlayer.js H5播放器是一款功能强大的H5视频播放器&#xff0c;它支持多种流媒体协议播放&#xff0c;包括WebSocket-FLV、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WebRTC等格式的视频流。它不仅支持H.264和H.265编码格式&#xff0c;还具备实时录像、低延时直播等功能…

springboot基于SpringBoot的旅游网站的设计与实现

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;旅游网站当然也不能排除在外&#xff0c;随着旅游网站的不断成熟&#xff0c;它彻底改变了过去传统的旅游网站方式&#xff0c;不仅使旅游管理…

聚划算!Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测

聚划算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测 目录 聚划算&#xff01;Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 聚划算&#xff01;Tran…

学习笔记——MathType公式编号:右编号和随章节变化

1.如何在word文档中插入带有编号的公式&#xff1f; 步骤&#xff1a;(前提是已经安装mathtype) 2.MathType公式编号怎么随章节变化&#xff1f; 想要编号级数也随标题级数进行自动变化&#xff0c;则需要插入或修改文档的“分隔符” 步骤&#xff1a;

应用程序知识:什么是企业应用程序?

企业应用程序简介 企业应用程序是一种具有特定用途并帮助员工完成工作的软件&#xff0c;例如文字处理、人力资源管理以及应收账款和应付账款。这些应用程序规模庞大&#xff0c;可让您的整个组织改善内部工作流程和客户服务流程。例如&#xff0c;企业应用程序允许您&#xf…

sublime可以写python吗

首先你需要安装一个Sublime Text&#xff08;http://www.sublimetext.com/&#xff09;和一个Python&#xff08;https://www.python.org/downloads/&#xff09;&#xff0c; 接下来打开Sublime Text&#xff1a; 1、如下图所示&#xff0c;点击菜单栏中的Tools —> Buil…

发布一个npm组件库包

Webpack 配置 (webpack.config.js) const path require(path); const MiniCssExtractPlugin require(mini-css-extract-plugin); const CssMinimizerPlugin require(css-minimizer-webpack-plugin); const TerserPlugin require(terser-webpack-plugin);module.exports {…