SpringBoot2.x 集成 Swagger3

Swagger是一款RESTFUL接口的文档在线自动生成加功能测试的软件,提供描述、生产、消费和可视化RESTful Web Service。Swagger也是一个api文档维护组织,后来成为了OpenAPI(一个业界的api文档标准)标准的主要定义者,现在最新的版本为17年发布的Swagger3(OpenAPI3)。基于OpenAPI2的Swagger2已于2017年停止维护。

SpringFox是Spring社区维护的一个项目(非官方),帮助使用者将Swagger2集成到Spring中。常用于Spring中帮助开发者生成文档,并可以轻松的在SpringBoot中使用,目前已经支持OpenAPI3标准。

本文主要对SpringBoot2.x集成Swagger3进行简单总结,通过引入SpringFox来使用Swagger3,其中SpringBoot使用的2.4.5版本。

一、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>
<!-- lombok插件 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

二、编写配置类

package com.rtxtitanv.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Collections;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.config.Swagger3Config
 * @description Swagger3配置类
 * @date 2021/6/7 12:47
 */
@Configuration
@EnableOpenApi
public class Swagger3Config {

    /**
     * 配置默认分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket defaultApi() {
        // DocumentationType.OAS_30为Swagger3的文档类型
        return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo()).select()
            /*
             * 通过apis和paths方法设置ApiSelector的构建规则
             * ApiSelectorBuilder apis(Predicate<RequestHandler> selector)
             *     RequestHandlerSelectors.any():构建所有API
             *     RequestHandlerSelectors.none():所有API都不构建
             *     RequestHandlerSelectors.basePackage():构建指定包路径下的所有API
             *     RequestHandlerSelectors.withClassAnnotation():仅构建带有指定类注解的API
             *     RequestHandlerSelectors.withMethodAnnotation():仅构建带有指定方法注解的API
             * ApiSelectorBuilder paths(Predicate<String> selector)
             *     PathSelectors.any():构建所有请求路径的API
             *     PathSelectors.none():所有请求路径的API都不构建
             *     PathSelectors.regex():仅构建正则匹配的请求路径的API
             *     PathSelectors.ant():仅构建与ant模式匹配的API
             */
            .apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.other")).paths(PathSelectors.any())
            .build();
    }

    /**
     * 配置user分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket userApi() {
        return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo())
            // 通过securitySchemes和securityReferences方法进行JWT配置
            .securitySchemes(Collections.singletonList(HttpAuthenticationScheme.JWT_BEARER_BUILDER.name("JWT").build()))
            .securityContexts(Collections.singletonList(SecurityContext.builder()
                .securityReferences(Collections.singletonList(
                    SecurityReference.builder().scopes(new AuthorizationScope[0]).reference("JWT").build()))
                .operationSelector(operationContext -> operationContext.requestMappingPattern().matches("/.*"))
                .build()))
            .select().apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.user"))
            // groupName方法设置分组名称
            .paths(PathSelectors.regex("/user/.*")).build().groupName("user");
    }

    /**
     * 配置order分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket orderApi() {
        return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo())
            .securitySchemes(Collections.singletonList(HttpAuthenticationScheme.JWT_BEARER_BUILDER.name("JWT").build()))
            .securityContexts(Collections.singletonList(SecurityContext.builder()
                .securityReferences(Collections.singletonList(
                    SecurityReference.builder().scopes(new AuthorizationScope[0]).reference("JWT").build()))
                .operationSelector(operationContext -> operationContext.requestMappingPattern().matches("/.*"))
                .build()))
            .select().apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.order"))
            .paths(PathSelectors.regex("/order/.*")).build().groupName("order");
    }

    /**
     * 构建API文档基本信息
     *
     * @return springfox.documentation.service.ApiInfo
     */
    private ApiInfo apiInfo() {
        // 联系人信息:分别为作者、主页、邮箱
        Contact contact = new Contact("RtxTitanV", "https://blog.csdn.net/RtxTitanV", "RtxTitanV@xxx.com");
        // 构建API文档的详细信息:依次为API标题、API描述、API联系人信息、API版本、API许可、API许可Url
        return new ApiInfoBuilder().title("SpringBoot2.x 集成 Swagger3").description("SpringBoot2.x 集成 Swagger3 测试文档")
            .contact(contact).version("1.0.0").license("Apache 2.0")
            .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0").build();
    }
}

配置类中每一个Docket对象都代表一个分组,Bean的名称不能相同,通过调用groupName(String groupName)方法来设置分组名。以上配置了三个分组,分别为default、user、order三组,其中没有调用groupName(String groupName)的Docket对象是默认分组配置,不同的分组可以配置不同的API文档基本信息(文档标题、描述、联系人信息等),这里主要是测试,就都配置的统一的API文档基本信息。另外user和order分组都配置了JWT认证,配置方式与Swagger2有所不同,配置JWT认证后在API文档中认证了Token之后,每次请求时在请求头都会携带该token。

Doket类的方法Docket enable(boolean externallyConfiguredFlag)可以控制Swagger的开启和关闭,可以根据不同环境进行动态的配置。配置项springfox.documentation.enabled也可以控制Swagger的开启与关闭,设置为false可以关闭Swagger。

三、创建实体类

package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.User
 * @description 用户实体类
 * @date 2021/6/7 12:41
 */
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "User", description = "用户信息")
public class User implements Serializable {

    private static final long serialVersionUID = -583023130164283193L;
    @ApiModelProperty(value = "用户id")
    private Long id;
    @ApiModelProperty(value = "用户名")
    private String username;
    @ApiModelProperty(value = "密码")
    private String password;
}
package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.Order
 * @description 订单实体类
 * @date 2021/6/8 11:30
 */
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "Order", description = "订单信息")
public class Order implements Serializable {

    private static final long serialVersionUID = -688022729580581140L;
    @ApiModelProperty(value = "订单id")
    private Long id;
    @ApiModelProperty(value = "订单编号")
    private String orderNumber;
    @ApiModelProperty(value = "订单描述")
    private String orderDescription;
    @ApiModelProperty(value = "订单所属用户id")
    private Long userId;
}
package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.CommonResult
 * @description 通用响应类
 * @date 2021/6/8 10:45
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "CommonResult", description = "通用响应对象")
public class CommonResult<T> implements Serializable {

    private static final long serialVersionUID = 2622902426607852032L;
    @ApiModelProperty(value = "响应码", required = true)
    private int code;
    @ApiModelProperty(value = "响应信息", required = true)
    private String message;
    @ApiModelProperty(value = "响应数据")
    private T data;

    public static <T> CommonResult<T> success(String message, T data) {
        return new CommonResult<>(HttpStatus.OK.value(), message, data);
    }

    public static <T> CommonResult<T> invalidParameter(String message) {
        return new CommonResult<>(HttpStatus.BAD_REQUEST.value(), message, null);
    }

    public static <T> CommonResult<T> notFound(String message) {
        return new CommonResult<>(HttpStatus.NOT_FOUND.value(), message, null);
    }
}

四、Controller层

分组default中的用于简单测试的Controller:

package com.rtxtitanv.controller.other;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
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;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.other.TestController
 * @description TestController
 * @date 2021/6/7 12:47
 */
@Api(value = "TestController", tags = "简单测试", description = "用于简单测试的API")
@RequestMapping("/test")
@RestController
public class TestController {

    @ApiOperation(value = "hello", notes = "一个简单的测试接口")
    @ApiImplicitParam(value = "名称", name = "name", required = true, defaultValue = "world",
        dataTypeClass = String.class)
    @GetMapping("/hello/{name}")
    public String hello(@PathVariable(value = "name") String name) {
        return "hello " + name;
    }
}

分组user中的Controller:

package com.rtxtitanv.controller.user;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import com.rtxtitanv.service.UserService;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.user.UserController
 * @description UserController
 * @date 2021/6/7 12:47
 */
@Api(value = "用户模块", tags = "用户管理", description = "用户的增删改查", protocols = "http")
@RequestMapping("/user")
@RestController
public class UserController {

    @Resource
    private UserService userService;

    @ApiOperation(value = "创建用户", notes = "根据User对象保存用户")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataTypeClass = Long.class,
            paramType = "query"),
        @ApiImplicitParam(value = "用户名", name = "username", required = true, defaultValue = "admin",
            dataTypeClass = String.class, paramType = "query"),
        @ApiImplicitParam(value = "密码", name = "password", required = true, defaultValue = "admin",
            dataTypeClass = String.class, paramType = "query")})
    @ApiResponses(value = {@ApiResponse(code = 200, message = "保存用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @PostMapping("/save")
    public CommonResult<User> saveUser(@ApiIgnore User user) {
        return userService.saveUser(user);
    }

    @ApiOperation(value = "查询所有用户", notes = "查询所有用户")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询所有用户成功")})
    @GetMapping("/findAll")
    public CommonResult<List<User>> findUserAll() {
        return userService.findUserAll();
    }

    @ApiOperation(value = "根据id查询用户", notes = "查询指定id用户")
    @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataTypeClass = Long.class)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id查询用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @GetMapping("/findById/{id}")
    public CommonResult<User> findUserById(@PathVariable(value = "id") Long id) {
        return userService.findUserById(id);
    }

    @ApiOperation(value = "根据id更新用户", notes = "更新指定id用户")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id更新用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @PutMapping("/updateById")
    public CommonResult<User> updateUserById(@RequestBody User user) {
        return userService.updateUserById(user);
    }

    @ApiOperation(value = "根据id删除用户", notes = "删除指定id用户")
    @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataTypeClass = Long.class)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id删除用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteById/{id}")
    public CommonResult<User> deleteUserById(@PathVariable(value = "id") Long id) {
        return userService.deleteUserById(id);
    }

    @ApiOperation(value = "删除所有用户", notes = "删除所有用户")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "删除所有用户成功"),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteAll")
    public CommonResult<List<User>> deleteUserAll() {
        return userService.deleteUserAll();
    }
}

分组order中的Controller:

package com.rtxtitanv.controller.order;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;
import com.rtxtitanv.service.OrderService;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.order.OrderController
 * @description OrderController
 * @date 2021/6/8 11:31
 */
@Api(value = "订单模块", tags = "订单管理", description = "订单的增删改查", protocols = "http")
@RequestMapping("/order")
@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @ApiOperation(value = "创建订单", notes = "根据Order对象保存订单")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(value = "订单id", name = "id", required = true, defaultValue = "1", dataTypeClass = Long.class,
            paramType = "query"),
        @ApiImplicitParam(value = "订单编号", name = "orderNumber", required = true, defaultValue = "780829537365759918",
            dataTypeClass = String.class, paramType = "query"),
        @ApiImplicitParam(value = "订单描述", name = "orderDescription", required = true, defaultValue = "二两牛肉面,微辣,多放点香菜",
            dataTypeClass = String.class, paramType = "query"),
        @ApiImplicitParam(value = "订单所属用户id", name = "userId", required = true, defaultValue = "1",
            dataTypeClass = Long.class, paramType = "query")})
    @ApiResponses(value = {@ApiResponse(code = 200, message = "保存订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @PostMapping("/save")
    public CommonResult<Order> saveOrder(@ApiIgnore Order order) {
        return orderService.saveOrder(order);
    }

    @ApiOperation(value = "查询所有订单", notes = "查询所有订单")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询所有订单成功")})
    @GetMapping("/finAll")
    public CommonResult<List<Order>> findOrderAll() {
        return orderService.findOrderAll();
    }

    @ApiOperation(value = "根据id更新订单", notes = "更新指定id订单")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id更新订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "订单不存在", response = CommonResult.class)})
    @PutMapping("/updateById")
    public CommonResult<Order> updateOrderById(@RequestBody Order order) {
        return orderService.updateOrderById(order);
    }

    @ApiOperation(value = "根据id删除订单", notes = "删除指定id订单")
    @ApiImplicitParam(value = "订单id", name = "id", required = true, defaultValue = "1", dataTypeClass = Long.class)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id删除订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "订单不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteById/{id}")
    public CommonResult<Order> deleteOrderById(@PathVariable(value = "id") Long id) {
        return orderService.deleteOrderById(id);
    }
}

这里仍然使用的Swagger的文档注解,是因为Swagger3(OpenAPI3)的@Tag注解有点问题没有生效,在Swagger-UI上,Controller上@Tagname属性表示的Controller的标题在和增删改查接口是分开的,并没有在同一个折叠面板中,所以还是换回了Swagger2的注解。

五、Service层

package com.rtxtitanv.service;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;

import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.UserService
 * @description UserService
 * @date 2021/6/7 12:48
 */
public interface UserService {

    /**
     * 保存用户
     *
     * @param user 用户参数
     * @return CommonResult<User>
     */
    CommonResult<User> saveUser(User user);

    /**
     * 查询所有用户
     *
     * @return CommonResult<List<User>>
     */
    CommonResult<List<User>> findUserAll();

    /**
     * 根据id查询用户
     *
     * @param id 用户id
     * @return CommonResult<User>
     */
    CommonResult<User> findUserById(Long id);

    /**
     * 根据id更新用户
     *
     * @param user 用户参数
     * @return CommonResult<User>
     */
    CommonResult<User> updateUserById(User user);

    /**
     * 根据id删除用户
     *
     * @param id 用户id
     * @return CommonResult<User>
     */
    CommonResult<User> deleteUserById(Long id);

    /**
     * 删除所有用户
     *
     * @return CommonResult<List<User>>
     */
    CommonResult<List<User>> deleteUserAll();
}
package com.rtxtitanv.service.impl;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import com.rtxtitanv.service.UserService;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.impl.UserServiceImpl
 * @description UserService实现类
 * @date 2021/6/7 12:49
 */
@Service
public class UserServiceImpl implements UserService {

    /**
     * 创建线程安全的Map,模拟用户信息的存储
     */
    private static final Map<Long, User> USER_MAP = Collections.synchronizedMap(new HashMap<>());

    @Override
    public CommonResult<User> saveUser(User user) {
        if (user.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        USER_MAP.put(user.getId(), user);
        return CommonResult.success("保存用户成功", user);
    }

    @Override
    public CommonResult<List<User>> findUserAll() {
        return CommonResult.success("查询所有用户成功", new ArrayList<>(USER_MAP.values()));
    }

    @Override
    public CommonResult<User> findUserById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        return CommonResult.success("根据id查询用户成功", USER_MAP.get(id));
    }

    @Override
    public CommonResult<User> updateUserById(User user) {
        if (user.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (USER_MAP.get(user.getId()) == null) {
            throw new NotFoundException("用户不存在");
        }
        user = USER_MAP.get(user.getId()).setUsername(user.getUsername()).setPassword(user.getPassword());
        return CommonResult.success("根据id更新用户成功", user);
    }

    @Override
    public CommonResult<User> deleteUserById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (USER_MAP.get(id) == null) {
            throw new NotFoundException("用户不存在");
        }
        return CommonResult.success("根据id删除用户成功", USER_MAP.remove(id));
    }

    @Override
    public CommonResult<List<User>> deleteUserAll() {
        if (USER_MAP.isEmpty()) {
            throw new NotFoundException("用户不存在");
        }
        ArrayList<User> users = new ArrayList<>(USER_MAP.values());
        USER_MAP.clear();
        return CommonResult.success("删除所有用户成功", users);
    }
}
package com.rtxtitanv.service;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;

import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.OrderService
 * @description OrderService
 * @date 2021/6/8 17:18
 */
public interface OrderService {

    /**
     * 保存订单
     *
     * @param order 订单参数
     * @return CommonResult<Order>
     */
    CommonResult<Order> saveOrder(Order order);

    /**
     * 查询所有订单
     *
     * @return CommonResult<List<Order>>
     */
    CommonResult<List<Order>> findOrderAll();

    /**
     * 根据id更新订单
     *
     * @param order 订单参数
     * @return CommonResult<Order>
     */
    CommonResult<Order> updateOrderById(Order order);

    /**
     * 根据id删除订单
     *
     * @param id 订单id
     * @return CommonResult<Order>
     */
    CommonResult<Order> deleteOrderById(Long id);
}
package com.rtxtitanv.service.impl;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;
import com.rtxtitanv.service.OrderService;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.impl.OrderServiceImpl
 * @description OrderService实现类
 * @date 2021/6/8 17:18
 */
@Service
public class OrderServiceImpl implements OrderService {

    /**
     * 创建线程安全的Map,模拟订单信息的存储
     */
    private static final Map<Long, Order> ORDER_MAP = Collections.synchronizedMap(new HashMap<>());

    @Override
    public CommonResult<Order> saveOrder(Order order) {
        if (order.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        ORDER_MAP.put(order.getId(), order);
        return CommonResult.success("保存订单成功", order);
    }

    @Override
    public CommonResult<List<Order>> findOrderAll() {
        return CommonResult.success("查询所有订单成功", new ArrayList<>(ORDER_MAP.values()));
    }

    @Override
    public CommonResult<Order> updateOrderById(Order order) {
        if (order.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (ORDER_MAP.get(order.getId()) == null) {
            throw new NotFoundException("订单不存在");
        }
        order = ORDER_MAP.get(order.getId()).setOrderNumber(order.getOrderNumber())
            .setOrderDescription(order.getOrderDescription()).setUserId(order.getUserId());
        return CommonResult.success("根据id更新订单成功", order);
    }

    @Override
    public CommonResult<Order> deleteOrderById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (ORDER_MAP.get(id) == null) {
            throw new NotFoundException("订单不存在");
        }
        return CommonResult.success("根据id删除订单成功", ORDER_MAP.remove(id));
    }
}

六、全局异常处理

自定义异常类:

package com.rtxtitanv.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.exception.InvalidParameterException
 * @description 自定义异常类,无效参数异常
 * @date 2021/6/8 18:56
 */
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class InvalidParameterException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = 1987968660448772948L;
    private String message;
}
package com.rtxtitanv.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.exception.NotFoundException
 * @description 自定义异常类,数据不存在的异常
 * @date 2021/6/8 18:56
 */
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class NotFoundException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = -7962596135697516276L;
    private String message;
}

全局异常处理类:

package com.rtxtitanv.handler;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.handler.GlobalExceptionHandler
 * @description 全局异常处理类
 * @date 2021/6/8 18:56
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(InvalidParameterException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public CommonResult<Object> invalidParameterException(InvalidParameterException e) {
        String message = e.getMessage();
        LOGGER.error("异常信息:" + message, e);
        return CommonResult.invalidParameter(message);
    }

    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    public CommonResult<Object> notFoundException(NotFoundException e) {
        String message = e.getMessage();
        LOGGER.error("异常信息:" + message, e);
        return CommonResult.notFound(message);
    }
}

七、运行项目查看文档

启动SpringBoot项目,访问Swagger-UI的地址http://localhost:8080/swagger-ui/查看接口文档:
1
切换到user分组:
2
3
切换到order分组:
4
创建订单接口如下:
5
创建订单接口的响应信息如下:
6
更新订单接口如下:
7
订单管理API的Schema:
8

八、测试接口

可以在API文档中进行接口测试。首先测试创建订单接口以保存订单,与Swagger2不同,在配置了JWT安全认证之后,页面右侧多了个Authorize按钮,点击该按钮,输入Token认证后,每次请求都会将该Token加到Authorization请求头中来访问后台资源:
9
点击Try it out按钮,输入参数后点击Execute:
10
测试结果如下,可见保存成功:
11
在进行查询测试前再保存2个数据,然后测试查询所有订单:
12
测试根据id更新订单接口:
13
测试根据id删除订单接口:
14
下面测试抛出自定义异常的情况,测试根据id删除订单接口,输入一个无效id:
15
测试根据id更新订单接口,更新一个不存在的订单:
16

九、引入knife4j

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。前身是swagger-bootstrap-ui。

首先引入依赖:

<!-- knife4j 起步依赖 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

注意:knife4j的版本需要根据引入的springfox版本而定。如果底层依赖的springfox框架版本是2.9.2,knife4j的版本使用2.02.0.5。如果底层springfox框架版本升级至`2.10.5`,OpenAPI规范是v2,knife4j的版本使用2.0.6。如果底层依赖springfox框架版本升级至3.0.3,OpenAPI规范是v3,knife4j的版本使用3.0~。

重启SpringBoot项目,访问knife4j的地址http://localhost:8080/doc.html查看接口文档:
17

代码示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RtxTitanV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值