Featured image of post Spring Boot RESTful API 常规实践

Spring Boot RESTful API 常规实践

兵无常势,水无常形。

相关文章

【合集】EasyExcel-读取excel

【合集】EasyExcel-写出excel

本文使用的版本

springboot 3.3.1

壳子

Controller层

方法命名归约

  1. 增:add
  2. 删:delete
  3. 改:modify
  4. 查:query
1
2
3
4
5
6
7
8
9
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo")
@Tag(name = "示例", description = "示例相关接口")
class DemoController {
    private final DemoService demoService;
}

Service层

service / DAO 层方法命名规约

  1. 获取单个对象的方法用 get 做前缀。
  2. 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save / insert 做前缀。
  5. 删除的方法用 remove / delete 做前缀。
  6. 修改的方法用 update 做前缀。
1
2
public interface DemoService {
}
1
2
3
4
5
6
7
@Slf4j
@Service
@RequiredArgsConstructor
public class DemoServiceImpl implements DemoService {
        private final DemoMapper demoMapper;
        private final DemoRepository demoRepository;
}

Mapper层(DAO层)

service / DAO 层方法命名规约

  1. 获取单个对象的方法用 get 做前缀。
  2. 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save / insert 做前缀。
  5. 删除的方法用 remove / delete 做前缀。
  6. 修改的方法用 update 做前缀。
1
2
3
@Mapper
public interface DemoMapper {
}

Get请求

分页查询(Query传参)

Mybatis写法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Operation(summary = "查询")
@GetMapping("")
public PageInfo<DemoVO> queryList(
        @Parameter(description = "页码,不传查所有", example = "1") @Min(1) @RequestParam(required = false) Integer pageNum,
        @Parameter(description = "每页数量,不传查所有", example = "10") @Min(1) @Max(100) @RequestParam(required = false) Integer pageSize,
        @Parameter(description = "是否包含车队的预约记录, true:包含, false:不包含, 默认为true", example = "true") @RequestParam(required = false, defaultValue = "true") Boolean containFleet,
        @Parameter(description = "名称", example = "zhangSan") @RequestParam(required = false) String name,
        @Parameter(description = "上期结转电量", example = "1.0") @RequestParam(required = false) BigDecimal lastEle,
        @Parameter(description = "开始日期", example = "1970-01-01") @RequestParam(required = false) LocalDate startDate,
        @Parameter(description = "结束日期", example = "1970-01-01") @RequestParam(required = false) LocalDate endDate,
        @Schema(description = "开始时间", example = "00:00:00", type = "string", format = "time") @RequestParam(required = false) LocalTime startTime,
        @Schema(description = "结束时间", example = "00:00:00", type = "string", format = "time") @RequestParam(required = false) LocalTime endTime,
        @Parameter(description = "开始日期时间", example = "1970-01-01T00:00:00Z") @RequestParam(required = false) ZonedDateTime startDateTime,
        @Parameter(description = "结束日期时间", example = "1970-01-01T00:00:00Z") @RequestParam(required = false) ZonedDateTime endDateTime,
        @Parameter(description = "用户活跃") @RequestParam(required = false) UserStatusEnum userStatus,
        @Parameter(description = "用户类型") @RequestParam(required = false, name = "userType") List<String> userTypeList) {
    List<DemoVO> demoVoList = demoService.list(pageNum, pageSize, name, startDate, endDate, startTime, endTime, startDateTime, endDateTime, userTypeList);
    return new PageInfo<>(demoVoList);
}
1
2
3
4
5
6
7
 List<DemoVO> list(@Nullable Integer pageNum, 
                  @Nullable Integer pageSize,
                  String name,
                  LocalDate startDate, LocalDate endDate,
                  LocalTime startTime, LocalTime endTime,
                  ZonedDateTime startDateTime, ZonedDateTime endDateTime,
                  List<String> userType);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public List<DemoVO> list(@Nullable Integer pageNum,
                         @Nullable Integer pageSize,
                         String name,
                         LocalDate startDate, LocalDate endDate,
                         LocalTime startTime, LocalTime endTime,
                         ZonedDateTime startDateTime, ZonedDateTime endDateTime,
                         List<String> userTypeList) {
    // 页码 和 每页数量 都不为空 才分页
    if (pageNum != null && pageSize != null) {
        if (pageNum <= 0) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "pageNum必须大于0");
        }
        if (pageSize <= 0) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "pageSize必须大于0");
        }
        PageHelper.startPage(pageNum, pageSize);
    }
    
    return demoMapper.selectPage(name, startDate, endDate, startTime, endTime, startDateTime, endDateTime, userTypeList);
}
1
2
3
4
5
List<DemoVO> selectPage(@Param("name") String name,
                        @Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate,
                        @Param("startTime") LocalTime startTime, @Param("endTime") LocalTime endTime,
                        @Param("startDateTime") ZonedDateTime startDateTime, @Param("endDateTime") ZonedDateTime endDateTime,
                        @Param("userTypeList") List<String> userTypeList);
 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
<select id="selectPage" resultType="com.zx.alice.pojo.vo.DemoVO">
    select
    d.id,
    d.last_ele,
    d.start_date,
    d.end_date,
    d.start_time,
    d.end_time,
    d.start_date_time,
    d.end_date_time,
    d.user_type
    from demo d
    where d.deleted = 0
    <if test="startDateTime != null and endDateTime != null">
        and d.end_date_time between #{startDateTime} and #{endDateTime}
    </if>
    <if test="userTypeList != null and userTypeList.size() >0">
        and d.user_type in
        <foreach collection="userTypeList" item="userType" index="index" open="(" close=")" separator=",">
            #{userType}
        </foreach>
    </if>
    <!-- 在d.marks中是否能匹配到markList中一个元素。如markList为集合[1,2,3] d.star_mark_ids存值示例:1,2,3-->
    <if test="markIdList != null and markIdList.size() > 0">
        and
        <foreach item="markId" collection="markIdList" open="(" separator="or" close=")">
            (find_in_set(#{markId}, d.mark_id) > 0)
        </foreach>
    </if>
</select>

JPA写法

先查询所有,再逻辑分页

  1. service查询所有
 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
31
32
33
34
35
@Override
public List<DemoVO> list(@Nullable Integer pageNum,
                         @Nullable Integer pageSize,
                         String name,
                         LocalDate startDate, LocalDate endDate,
                         LocalTime startTime, LocalTime endTime,
                         ZonedDateTime startDateTime, ZonedDateTime endDateTime,
                         List<String> userTypeList) {

    List<Demo> noDeletedList = demoRepository.findNoDeleted();
    List<DemoVO> demoVOList = noDeletedList
            .stream()
            .map(demo -> {
                DemoVO demoVO = new DemoVO();
                demoVO.setId(demo.getId());
                demoVO.setName(demo.getName());
                demoVO.setLastEle(demo.getLastEle());
                demoVO.setStartDate(demo.getStartDate());
                demoVO.setEndDate(demo.getEndDate());
                demoVO.setStartTime(demo.getStartTime());
                demoVO.setEndTime(demo.getEndTime());
                demoVO.setStartDateTime(demo.getStartDateTime().atZone(ZoneId.systemDefault()));
                demoVO.setEndDateTime(demo.getEndDateTime().atZone(ZoneId.systemDefault()));
                demoVO.setCreateDateTime(demo.getCreateDateTime().atZone(ZoneId.systemDefault()));
                demoVO.setCreateBy(demo.getCreateBy());
                demoVO.setUpdateDateTime(demo.getUpdateDateTime().atZone(ZoneId.systemDefault()));
                demoVO.setUserType(demo.getUserType());
                demoVO.setDeleted(demo.getDeleted());
                demoVO.setUserStatus(demo.getUserStatus());
                return demoVO;
            })
            .collect(Collectors.toList());

    return demoVOList;
}
  1. controller层逻辑分页

参考 Pagehelper逻辑分页

1
PageInfo<Demo> demoPageInfo = logicPagination(pageNum, pageSize, list); 

excel文件 导出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Operation(summary = "excel文件 导出")
@GetMapping("/excel")
public void queryExcel(
        HttpServletResponse response,
        @Parameter(description = "页码,不传查所有", example = "1") @Min(1) @RequestParam(required = false) Integer pageNum,
        @Parameter(description = "每页数量,不传查所有", example = "10") @Min(1) @Max(100) @RequestParam(required = false) Integer pageSize,
        @Parameter(description = "名称", example = "zhangSan") @RequestParam(required = false) String name,
        @Parameter(description = "上期结转电量", example = "1.0") @RequestParam(required = false) BigDecimal lastEle,
        @Parameter(description = "开始日期", example = "1970-01-01") @RequestParam(required = false) LocalDate startDate,
        @Parameter(description = "结束日期", example = "1970-01-01") @RequestParam(required = false) LocalDate endDate,
        @Schema(description = "开始时间", example = "00:00:00", type = "string", format = "time") @RequestParam(required = false) LocalTime startTime,
        @Schema(description = "结束时间", example = "00:00:00", type = "string", format = "time") @RequestParam(required = false) LocalTime endTime,
        @Parameter(description = "开始日期时间", example = "1970-01-01T00:00:00Z") @RequestParam(required = false) ZonedDateTime startDateTime,
        @Parameter(description = "结束日期时间", example = "1970-01-01T00:00:00Z") @RequestParam(required = false) ZonedDateTime endDateTime,
        @Parameter(description = "用户活跃") @RequestParam(required = false) UserStatusEnum userStatus,
        @Parameter(description = "用户类型") @RequestParam(required = false, name = "userType") List<String> userTypeList) {
    demoService.getExcel(response, pageNum, pageSize, name, startDate, endDate, startTime, endTime, startDateTime, endDateTime, userTypeList);
}
1
2
3
4
5
6
7
8
void getExcel(HttpServletResponse response,
              @Nullable Integer pageNum,
              @Nullable Integer pageSize,
              String name,
              LocalDate startDate, LocalDate endDate,
              LocalTime startTime, LocalTime endTime,
              ZonedDateTime startDateTime, ZonedDateTime endDateTime,
              List<String> userType);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Override
public void getExcel(HttpServletResponse response,
                     @Nullable Integer pageNum,
                     @Nullable Integer pageSize,
                     String name,
                     LocalDate startDate, LocalDate endDate,
                     LocalTime startTime, LocalTime endTime,
                     ZonedDateTime startDateTime, ZonedDateTime endDateTime,
                     List<String> userTypeList) {
    List<DemoVO> demoVoList = list(null, null, name, startDate, endDate, startTime, endTime, startDateTime, endDateTime, userTypeList);
  
    // 第三方excel框架导出
}

第三方excel框架导出

【合集】EasyExcel-读取excel

批量查询(根据主键 + Path路径传参)

1
2
3
4
5
@Operation(summary = "查询")
@GetMapping("/{id}")
public List<DemoVO> query(@Parameter(description = "主键") @PathVariable(name = "id") List<Long> idList) {
    return demoService.get(idList);
}
1
List<DemoVO> get(@Nullable List<Long> idList);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Override
public List<DemoVO> get(@Nullable List<Long> idList) {
    if (ObjectUtils.isEmpty(idList)) {
        return Collections.emptyList();
    }
    return idList.stream().map(id -> {
        Demo demo = demoRepository.findById(id).orElse(new Demo());
        DemoVO demoVO = new DemoVO();
        BeanUtils.copyProperties(demo, demoVO);
        return demoVO;
    }).toList();
}

Put请求

单个修改(根据主键)

1
2
3
4
5
@Operation(summary = "修改")
@PutMapping("")
public void modify(@RequestBody @Valid DemoDTO dto) {
    demoService.update(dto);
}
1
void update(@Nullable DemoDTO dto);
1
2
3
4
5
6
@Override
public void update(@Nullable DemoDTO dto) {
    if (dto == null) {
        return;
    }
}

批量修改(根据主键)

1
2
3
4
5
@Operation(summary = "修改")
@PutMapping("")
public void modify(@RequestBody @Valid List<DemoDTO> dtoList) {
    demoService.update(dtoList);
}
1
void update(@Nullable List<DemoDTO> dtoList);
1
2
3
4
5
6
@Override
public void update(@Nullable List<DemoDTO> dtoList) {
    if (ObjectUtils.isEmpty(dtoList)) {
        return;
    }
}

Post请求

单个新增

1
2
3
4
5
@Operation(summary = "新增")
@PostMapping("")
public void add(@RequestBody @Valid DemoDTO dto) {
    demoService.save(dto);
}
1
void save(@Nullable DemoDTO dto);
1
2
3
4
5
6
7
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void save(@Nullable DemoDTO dto) {
    if (dto == null) {
        return;
    }
}

批量新增

1
2
3
4
5
@Operation(summary = "新增")
@PostMapping("")
public void add(@RequestBody @Valid List<DemoDTO> dtoList) {
    demoService.save(dtoList);
}
1
void save(@Empty List<DemoDTO> dtoList);
1
2
3
4
5
6
7
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void save(@Nullable List<DemoDTO> dtoList) {
    if (ObjectUtils.isEmpty(dtoList)) {
        return;
    }
}

excel文件 导入

Controller层

@RequestParam List

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Operation(summary = "新增excel文件")
@PostMapping(value = "/excel", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel(@Parameter(description = "文件列表") @RequestParam(name = "file") List<MultipartFile> fileList,
                     @Parameter(description = "名称", example = "zhangSan") @RequestParam() String name) {
    for (MultipartFile file : fileList) {
        if (!List.of("application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet").contains(file.getContentType())) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "入参文件必须是Excel文件");
        }
    }
    demoService.saveExcel(fileList);
}

@RequestParam

1
2
3
4
5
@Operation(summary = "新增excel文件")
@PostMapping(value = "/excel2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel2(@Parameter(description = "单个文件") @RequestParam(name = "file") MultipartFile file,
                      @Parameter(description = "名称", example = "zhangSan") @RequestParam() String name) {
}

@RequestPart List

1
2
3
4
5
@Operation(summary = "新增excel文件")
@PostMapping(value = "/excel3", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel3(@Parameter(description = "文件列表") @RequestPart(name = "file") List<MultipartFile> fileList,
                      @Parameter(description = "名称", example = "zhangSan") @RequestParam() String name) {
}

@RequestPart

1
2
3
4
5
@Operation(summary = "新增excel文件")
@PostMapping(value = "/excel4", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel4(@Parameter(description = "单个文件") @RequestPart() MultipartFile file,
                      @Parameter(description = "名称", example = "zhangSan") @RequestParam() String name) {
}

json + form-data

不建议混合传,还是建议文件另外传,存单独的file表,解耦

1
2
3
4
5
6
7
8
@Operation(summary = "新增excel文件 ")
@PostMapping(value = "/excel5", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel5(
        @Parameter(description = "单个文件") @RequestPart MultipartFile file,
        @Parameter(description = "名称", example = "zhangSan") @RequestParam String name,
        @ModelAttribute DemoDTO dto) {
    // 注意:Dto的属性需要与表单字段名称匹配
}

jsonStr + form-data

不建议混合传,还是建议文件另外传,存单独的file表,解耦

1
2
3
4
5
6
7
8
9
@Operation(summary = "新增excel文件")
@PostMapping(value = "/excel6", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void addExcel6(
        @Parameter(description = "单个文件") @RequestPart MultipartFile file,
        @Parameter(description = "名称", example = "zhangSan") @RequestParam String name,
        @Parameter(description = "数据对象") @RequestPart String dtoJson) throws JsonProcessingException {
    // 在此方法内部,需要手动将dtoJson反序列化为DemoDTO对象
    DemoDTO dto = new ObjectMapper().readValue(dtoJson, DemoDTO.class);
}

Service层

1
void saveExcel(@Nullable List<MultipartFile> files);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Override
public void saveExcel(@Nullable List<MultipartFile> files) {
    if (ObjectUtils.isEmpty(files)) {
        return;
    }

    for (MultipartFile file : files) {
        // 第三方excele解析
    }
}

第三方excel框架导出

【合集】EasyExcel-读取excel

Delete请求

批量删除(根据主键 + Path路径传参)

1
2
3
4
5
@Operation(summary = "删除")
@DeleteMapping("/{id}")
public void delete(@Parameter(description = "主键") @PathVariable(name = "id") List<Long> idList) {
    demoService.remove(idList);
}
1
void remove(@Nullable List<Long> idList);
1
2
3
4
5
6
@Override
public void remove(@Nullable List<Long> idList) {
    if (ObjectUtils.isEmpty(idList)) {
        return;
    }
}

DemoDTO

 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
31
32
33
34
35
36
37
38
39
40
41
42
@Data
@Validated
@Schema(description = "示例信息")
public class DemoDTO {

    @Schema(description = "主键")
    private Long id;

    @NotBlank()
    @Schema(description = "张三")
    private String name;

    @NotNull()
    @Schema(description = "上期结转电量")
    private BigDecimal lastEle;

    @Schema(description = "开始日期")
    private LocalDate startDate;

    @Schema(description = "结束日期")
    private LocalDate endDate;

    @Schema(description = "开始时间", example = "00:00:00", type = "string", format = "time")
    private LocalTime startTime;

    @Schema(description = "结束时间", example = "00:00:00", type = "string", format = "time")
    private LocalTime endTime;

    @Schema(description = "开始日期时间")
    private ZonedDateTime startDateTime;

    @Schema(description = "结束日期时间")
    private ZonedDateTime endDateTime;

    @Schema(description = "创建日期时间")
    private ZonedDateTime createDateTime;

    @Hidden // swagger3隐藏字段
    @Schema(description = "swagger3隐藏字段")
    private String hidden;

}

Websocket

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<dependencies>  
    <!-- Spring Boot Starter Web, 它已经包含了Tomcat的WebSocket支持 -->  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-web</artifactId>  
    </dependency>  
  
    <!-- Spring Boot Starter Test -->  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-test</artifactId>  
        <scope>test</scope>  
    </dependency>  
  
    <!-- WebSocket 消息代理 -->  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-websocket</artifactId>  
    </dependency>  
</dependencies>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.context.annotation.Configuration;  
import org.springframework.messaging.simp.config.MessageBrokerRegistry;  
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;  
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;  
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;  
  
@Configuration  
@EnableWebSocketMessageBroker  
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {  
  
    @Override  
    public void registerStompEndpoints(StompEndpointRegistry registry) {  
        // 注册一个WebSocket的端点,客户端将连接到这个端点  
        registry.addEndpoint("/websocket").withSockJS();  
    }  
  
    @Override  
    public void configureMessageBroker(MessageBrokerRegistry registry) {  
        // 设置消息代理的前缀  
        registry.enableSimpleBroker("/topic");  
        // 设置应用程序的前缀,以便从WebSocket客户端接收消息  
        registry.setApplicationDestinationPrefixes("/app");  
    }  
}
 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
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        Thread.sleep(1000); // 模拟延迟  
        return new Greeting("Hello, " + message.getName() + "!");
    }
}

// DTO类  
public class HelloMessage {
    private String name;

    // getter 和 setter  
}

public class Greeting {
    private String content;

    // 构造器、getter 和 setter  
}
皖ICP备2024056275号-1
发表了80篇文章 · 总计150.57k字
本站已稳定运行