介绍

对于后端接口调用,接口文档十分重要,而写好一份接口文档却是一件难事。在大部分情况下,文档并不是一成不变的,定义好的接口也不是一成不变的,可能在开发中,接口文档的修改不止一次变化,这对于调用者以及编写者是很难受的一件事情。我们相信没人会真心愿意去写这份东西,在调用中,一个字母的错误,则会造成很大的麻烦。但是通过knife4j,我们可以每次修改接口都去文档中进行编写,而出错率近乎为零。只要在你写代码的过程中,加上几个注解,文档就会自动生成。

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
具体相关内容请阅读:Knife4j文档

相关接口注解API的使用

@Api

在控制层Controller中添加注解来描述接口信息

   @Api(tags = "企业信息表 控制层")
   @RestController
   @RequestMapping("enterprise")
   public class EnterpriseController {

效果图如下:

@ApiOperation

在控制层Controller方法中配置接口的标题信息

    @ApiOperation("获取单条记录")
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Reply<EnterpriseVO> query(String id) {
        return Reply.success(this.enterpriseService.selectById(id));
    }

效果图如下:

@ApiModel

定义在返回对象类上,用于描述返回对象的的意义

   @ApiModel("企业信息表")
   public class EnterpriseVO implements Serializable {

效果图如下:

@ApiModelProperty

定义在出入参数对象的字段上,用来描述对象属性。

    @ApiModelProperty("申请时间")
    private Date applytime;

效果图如下:

关于文档中”是否是必须”说明

文档中的”是否是必须”主要通过注解的required的属性进行设置。有这个属性的注解常见的主要有@ApiModelProperty@RequestParam@RequestBody

对于@ApiModelProperty在接口文档”是否是必须”的,我们看到默认是false的。我们来看看这个注解里面是怎么定义的。

//截取一部分代码,并没有完整展示

public @interface ApiModelProperty {
    String value() default "";

    String name() default "";

    boolean required() default false;
}

我们看到required属性默认是false的。

我们再来看看@RequestParam@RequestBody注解中required默认值是怎么样的:

//截取一部分代码,并没有完整展示

public @interface RequestParam {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;
}
public @interface RequestBody {
    boolean required() default true;
}

可以看到@RequestParam@RequestBody它们两个的required属性默认都是true,而对于@ApiModelProperty注解的required属性默认是false的。也就是说他们是冲突的,在实际开发中,我们应该注意一下这种情况的出现。

相关注解,具体参考下表:

作用范围 API 使用位置
协议集描述 @Api 用于controller类上
协议描述 @ApiOperation 用在controller的方法上
对象类 @ApiModel 用在对象返回类上
对象属性 @ApiModelProperty 用在出入参数对象的字段上
Response集 @ApiResponses 用在controller的方法上
Response @ApiResponse 用在 @ApiResponses里边
非对象参数集 @ApiImplicitParams 用在controller的方法上
非对象参数描述 @ApiImplicitParam 用在@ApiImplicitParams的方法里边

接口的调用问题

接口的调用主要分两种情况,一种是内部服务之间的相互调用,一种是外部调用服务。它们之间的区别在我们这里主要是:

  • 是否需要携带token去访问接口
    1.内部服务之间访问不需要携带token,只需要提供一个标识。
    2.外部调用服务需要携带token,否则访问失败。

内部服务之间调用

下面我举一个例子,我想要访问另外一个内部服务的接口 通过id获取员工数据

首先在 cn.flyrise.pai.example.feign包下声明一个service接口。
启动类中的@EnablePaiFeignClients("cn.flyrise.*")在扫描@FeignClient注解的时候:默认扫描cn.flyrise.**.feign这个包下的。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnablePaiFeignClients {
    String[] value() default {};

    String[] basePackages() default {"cn.flyrise.**.feign"};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

因为String[] basePackages() default {“cn.flyrise.**.feign”},这里已经声明了默认值。

操作示例

1. 首先通过@FeignClient来实现

name属性用于指定要访问的内部服务名称

@PostMapping指定访问该服务方法的URL地址。

@FeignClient(
    name = "pai-console"
)
    @ApiOperation("通过id获取员工数据")
    @PostMapping({"/info/test/getStaffByIds"})
    Reply<List<MessageStaffVO>> getStaffByIds(@RequestBody Collection<String> staffIds);
2. 看一下反编译后的方法是怎么样的。
    @ApiOperation("通过id获取员工数据")
    @PostMapping({"/info/test/getStaffByIds"})
    Reply<List<MessageStaffVO>> getStaffByIds(@RequestBody Collection<String> var1, @RequestHeader("from") String var2);

到这里你会发现多了一个 @RequestHeader(“from”) String var2。它就是我所说的一个标识。这个from标识,其实是一个常量,它让内部服务之间相互调用不需要提供token。

外部调用服务

路由问题

在说外部调用服务之前,我们先来讨论一个关于路由的问题。在一个本地工程部署成为应用服务后,它的路由会发送变化。

接口文档地址变化

访问接口的URL地址发送变化

/console-demo/getEntByIds

在成为应用服务之后:

/example-api/console-demo/getEntByIds

原因分析

Ingress的自动配置
目前项目分两种Ingress配置: 后端 API 服务、前端 UI 服务。

为了配合Pai-cli在执行部署(deploy命令)项目时,能自动生成Ingress路由配置,增加了新标签:

  • metadata.labels.ingress: front|service, 其中 front 表示前端 UI 服务项目, service 表后端 API 服务项目,技术中台在部署后会根据这个标签对 Ingress 进行更新操作
  • metadata.labels.path: demo-api, 其中demo-api的值仅用于指定路由规则的中path值的名称,如/demo-api/(.*)$中的 demo-api,不含其他表达式。特别注意:这个标签是一个可选项,不写则会用下述默认约定规则自动生成。
# 后端 API 服务
apiVersion: v1
kind: Service
metadata:
  name: pai-demo
  namespace: tt
  labels:
    app: pai-demo
    ingress: service
    path: demo-api
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: pai-demo

后端 API 服务项目名称默认约定为pai-demo,部署到 Ingress 路由后会变成/demo-api, 端口统一为8080, 示例如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/service-weight: ''
    # 绑定ingress-class
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: "/$1"

  name: pai-ingress
  namespace: tt
spec:
  rules:
    - host: pai.xxx.cn
      http:
        paths:
          - path: /demo-api/(.*)$
            backend:
              serviceName: pai-demo
              servicePort: 8080

token问题

我们可以看到,在作为外部去调用服务时,它需要填写一个请求头,该内容必须携带token值。

否则:

{
  "msg": "401 UNAUTHORIZED 令牌不合法,禁止访问 /findAppPermsByEnt",
  "code": "401",
  "time": 1601137636447
}
文档更新时间: 2024-03-25 11:23   作者:朱灿奕