关于jFinal集成Swagger的实践

前言:

    之前工作中使用的都是Spring框架,现在工作中用到JFinal,于是参考了JFinal的文档写了小demo,一步一步的走下去,难以理解并不常用的先行跳过了。

    在实际工作中,整理接口文档向来是一个比较费时的活儿。在SpringBoot中使用Swagger来整理接口文档,借鉴一篇《Jfinal集成Swagger 极简配置》的网上的实践,我自己实践JFinal集成Swagger并记录(期间遇到很多的坑)。限于自己的水平,如有错漏不当之处,还请不吝指教。

正文:

一、引入JFinal-Swagger依赖


  1. <dependency>

  2.             <groupId>live.autu</groupId>

  3.             <artifactId>jfinal-swagger</artifactId>

  4.             <version>1.0.0</version>

  5.         </dependency>

二、下载UI文件到项目中

    

    view.7z  下载地址:https://wwe.lanzous.com/ignP3j80xaf

    swagger.7z 下载地址:https://wwe.lanzous.com/iycnPj80x9e

    解压后复制进项目中

    大家可以参考我的目录配置:

    image.png

三、路由配置

    视图路径=BaseViewPath+viewPath+view

    setBaseViewPath()方法将后面的目录追加在src/main/webapp目录下(应该是指向了 undertow.resourcePath 配置中的第一个有效目录)。

public class SwaggerRoutes extends Routes {
    @Override
    public void config() {
        setBaseViewPath("/views");
        add("/swagger", SwaggerController.class,"");
    }
}
public void configRoute(Routes routes) {

    //配置路由
    routes.add(new SwaggerRoutes());
}

四、插件配置

    注册Swagger插件

    注意点:

    setSchemes设置swagger协议集合 http/https/...

    setHost中的套接字必须与undertow启动的host一致,不然会出现跨域问题(关于为什么localhost可以,而127.0.0.1不行,这个我不太清楚,网上查了也没有确切的回答

public void configPlugin(Plugins plugins) {
    plugins.add(new SwaggerPlugin(new SwaggerDoc().setBasePath("/").setHost("localhost:8097").
    setSchemes(Arrays.asList("http")).setSwagger("2.0")
        .setInfo(new SwaggerApiInfo("全宇宙最牛逼的Jfinal开发脚手架", "1.0", "Jfinal Swagger", ""))));
}

在undertow.txt中增加

undertow.hotSwapClassPrefix=live.autu.plugin.jfinal.swagger

至此,Swagger配置完成。

===========================================================================

以下展示我的undertow.txt的配置

# true undertow.devMode=true
undertow.port=8097
undertow.host=0.0.0.0

# swagger
undertow.hotSwapClassPrefix=live.autu.plugin.jfinal.swagger


# context path
#undertow.contextPath=/abc

undertow.resourcePath = src/main/webapp, classpath:static


# gzip undertow.gzip.enable=true
# -11 91 9 undertow.gzip.level=-1
# undertow.gzip.minLength=1024

# session #undertow.session.timeout=1800
# session session truedevMode#undertow.session.hotSwap=true


# ssl
undertow.ssl.enable=false
# ssl 443
undertow.ssl.port=443
# PKCS12
undertow.ssl.keyStoreType=PKCS12
# undertow.ssl.keyStore=demo.pfx
# undertow.ssl.keyStorePassword=123456
# undertow.ssl.keyAlias=demo

# ssl http2chrome : chrome://net-internals/#http2
undertow.http2.enable=true

# ssl http https
undertow.http.toHttps=false
# ssl http https 302
undertow.http.toHttpsStatusCode=302

# ssl http
undertow.http.disable=false


五、Swagger的使用

   这部分内容转载于:https://juejin.cn/post/6844903901724950535

@Api

用在类上,该注解将一个Controller(Class)标注为一个swagger资源(API)。在默认情况下,Swagger-Core只会扫描解析具有@Api注解的类,而会自动忽略其他类别资源(JAX-RS endpoints,Servlets等等)的注解。该注解包含以下几个重要属性

  • tags API分组标签。具有相同标签的API将会被归并在一组内展示。

  • value 如果tags没有定义,value将作为Api的tags使用

  • description API的详细描述,在1.5.X版本之后不再使用,但实际发现在2.0.0版本中仍然可以使用

@ApiOperation

在指定的(路由)路径上,对一个操作或HTTP方法进行描述。具有相同路径的不同操作会被归组为同一个操作对象。不同的HTTP请求方法及路径组合构成一个唯一操作。此注解的属性有:

  • value 对操作的简单说明,长度为120个字母,60个汉字。

  • notes 对操作的详细说明。

  • httpMethod HTTP请求的动作名,可选值有:"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH"。

  • code 默认为200,有效值必须符合标准的HTTP Status Code Definitions

@ApiImplicitParams

用在方法上,注解ApiImplicitParam的容器类,以数组方式存储。

@ApiImplicitParam

对API的单一参数进行注解。虽然注解@ApiParam同JAX-RS参数相绑定,但这个@ApiImplicitParam注解可以以统一的方式定义参数列表,也是在Servelet及非JAX-RS环境下,唯一的方式参数定义方式。注意这个注解@ApiImplicitParam必须被包含在注解@ApiImplicitParams之内。可以设置以下重要参数属性:

  • name 参数名称

  • value 参数的简短描述

  • required 是否为必传参数

  • dataType 参数类型,可以为类名,也可以为基本类型(String,int、boolean等)

  • paramType 参数的传入(请求)类型,可选的值有path, query, body, header or form。

@ApiParam

增加对参数的元信息说明。这个注解只能被使用在JAX-RS 1.x/2.x的综合环境下。其主要的属性有

  • required 是否为必传参数,默认为false

  • value 参数简短说明

@ApiResponses

注解@ApiResponse的包装类,数组结构。即使需要使用一个@ApiResponse注解,也需要将@ApiResponse注解包含在注解@ApiResponses内。

@ApiResponse

描述一个操作可能的返回结果。当REST API请求发生时,这个注解可用于描述所有可能的成功与错误码。可以用,也可以不用这个注解去描述操作的返回类型,但成功操作的返回类型必须在@ApiOperation中定义。如果API具有不同的返回类型,那么需要分别定义返回值,并将返回类型进行关联。但Swagger不支持同一返回码,多种返回类型的注解。注意:这个注解必须被包含在@ApiResponses注解中。

  • code HTTP请求返回码。有效值必须符合标准的HTTP Status Code Definitions

  • message 更加易于理解的文本消息

  • response 返回类型信息,必须使用完全限定类名,比如“com.xyz.cc.Person.class”。

  • responseContainer 如果返回类型为容器类型,可以设置相应的值。有效值为 "List", "Set" or "Map",其他任何无效的值都会被忽略。

Model注解

对于Model的注解,Swagger提供了两个:@ApiModel及@ApiModelProperty,分别用以描述Model及Model内的属性。

@ApiModel

描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)

提供对Swagger model额外信息的描述。在标注@ApiOperation注解的操作内,所有的类将自动被内省(introspected),但利用这个注解可以做一些更加详细的model结构说明。主要属性有:

  • value model的别名,默认为类名

  • description model的详细描述

@ApiModelProperty

描述一个model的属性

对model属性的注解,主要的属性值有:

  • value 属性简短描述

  • example 属性的示例值

  • required 是否为必须值

六、demo

1.数据库表 org_user(不太规范,如果按照Jfinal的标准应该字段小写并用下划线)

image.png

2.实体类

public class User extends Model<User> {
}

3.Controller

    注意点:

    dataType 的值首字母必须小写(如:integer,string,...),不然的话当设置为require=true时,校验会始终过不去。

    其实在Jfinal中ActionKey在配置了控制器路由后,可以不必配置。但是使用swagger时,swagger的swagger-api扫描被@Api注解的控制器时,找不到@ActionKey注解的方法时会抛异常,所以我这里配置了ActionKey

@Api(tags = {"Jfinal诸多特性实践控制层"})
public class TestSwaggerController extends Controller {
    /**
     *
     *   测试编码
     */


    @ApiOperation(methods = {RequestMethod.POST},description = "测试Swaggger",consumes = {"application/x-www-form-urlencoded"})
    @ApiImplicitParams({
            @ApiImplicitParam(name="NAME",description = "用户的姓名信息",required=true,dataType="string"),
            @ApiImplicitParam(name="AGE",description = "用户的年龄信息",required=true,dataType="integer",defaultValue = "0"),
            @ApiImplicitParam(name="USERID",description = "用户Id",required = true,dataType ="string",defaultValue =""),
            @ApiImplicitParam(name="CREATETIME",description = "创建时间",required = true,dataType = "string")
    })
    @ApiResponses({
            @ApiResponse(code="200",message = "成功"),
            @ApiResponse(code="3001",message = "系统异常")
    })
    @Before(Tx.class)
    @ActionKey("/sw/testSwagger")
    public void testSwagger(@Para("") User user){
        Map<String,Object> result=new HashMap<>();
        result.clear();
        try{
            String name = user.getStr("NAME");
            System.out.println(name);
            user.save();
            PublicMethod.success(result,user);
        }catch (Exception e){
            e.printStackTrace();
            PublicMethod.server_error(result);
            throw e;
        }finally {
            renderJson(result);
        }
    }
}

4.路由配置

public void configRoute(Routes routes) {

    //配置路由
    routes.add(new AdminRoutes());
    routes.add(new SwaggerRoutes());
}
public class AdminRoutes extends Routes {
    @Override
    public void config() {
        setBaseViewPath("/admin");
        add("/sw", TestSwaggerController.class);
    }
}
public class SwaggerRoutes extends Routes {
    @Override
    public void config() {
        setBaseViewPath("/views");
        add("/swagger", SwaggerController.class,"");
    }
}

5.启动并访问Swagger host/swagger

本例中:http://localhost:8097/swagger

image.png测试接口:

20201210105200.png

执行结果:

20201210105341.png

评论区

steven_lhcb_9527

2020-12-10 14:42

之前以为127.0.0.1和localhost一样的,知道出现了跨域的问题,才发现两者是不一样的

523039704

2020-12-16 15:03

你可以看看这个关于Swagger的方案
https://gitee.com/linbq-b7/jfinal-swagger-knife4j

steven_lhcb_9527

2020-12-16 15:57

@523039704 knife4j很妙。我再学习学习用法

steven_lhcb_9527

2020-12-17 08:52

@523039704 确实妙。就是配置的时候遇到很多坑

jiaxiang

2021-01-08 16:12

我觉得Jfinal集成swagger还是不太方便,我一般是用yapi来管理接口

steven_lhcb_9527

2021-01-08 17:04

是不方便,集成knife4j的还可以。yapi没用过