zoukankan      html  css  js  c++  java
  • Swagger结合mustache模板生成后台接口代码、以及前后台建模代码

    之前项目中使用的的thrift来建模,维护前后台模型以及rest接口,前台使用的是angular2; 

    但是使用thrift只能生成建模,后台的rest接口的Controller文件还是需要手动去写,一旦接口改动就会涉及到很多方面。

    由此准备使用Swagger和mustache模板来做一个maven插件直接生成前台ts文件和后台java文件以及rest接口文件。只需要维护swagger的yaml文件。

    yaml文件:

    swagger: "2.0"
    info:
    description: "This is a sample server Petstore server."
    version: "1.0.0"
    title: "Swagger Petstore"
    termsOfService: "http://swagger.io/terms/"
    contact:
    email: "apiteam@swagger.io"
    license:
    name: "Apache 2.0"
    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
    host: "petstore.swagger.io"
    basePath: "/v2"
    #tags Controller类名
    tags:
    - name: "UserRestApi"
    schemes:
    - "https"
    - "http"
    # paths rest接口相关信息
    paths:
    /user/{username}:
    #get 请求方式 post put...
    get:
    tags:
    - "UserRestApi"
    summary: "Get user by user name"
    description: ""
    #operationId:接口方法名
    operationId: "getUserByName"
    produces:
    - "application/xml"
    - "application/json"
    parameters:
    - name: "username"
    #in:path路径传参(占位符传参) body消息体传参 query问号传参 ...
    in: "path"
    description: "The name that needs to be fetched. Use user1 for testing. "
    required: true
    type: "string"
    responses:
    200:
    description: "successful operation"
    # schema $ref 自定义模型(非基础数据类型)
    schema:
    $ref: "#/definitions/User"
    400:
    description: "Invalid username supplied"
    404:
    description: "User not found"

    #definitions 前后台模型相关信息
    definitions:
    User:
    type: object
    properties:
    id:
    type: integer
    #int64 Long int32 Integer
    format: int64
    petId:
    type: integer
    format: int64
    quantity:
    type: integer
    format: int32
    shipDate:
    type: string
    format: date-time
    status:
    type: string
    description: Order Status
    enum:
    - placed
    - approved
    - delivered
    complete:
    type: boolean

    生成的Controller文件:

    package ni.jun.yang.api;
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import ni.jun.yang.api.bean.User;
    
    @RestController
    public class UserRestApi
    {
         //接口注入,逻辑均在实现类中实现
        UserRestApiHandle handle;
    
        @RequestMapping(value = "/user/{userName}",method = RequestMethod.GET)
        public User getUserByName(HttpServletRequest request, @PathVariable String userName) {
            //TODO
            return handle.getUserByName(request, username);
        }
    }

    同时还生成对应User类,UserRestApiHandle 接口,对于"/user/{username}"接口的逻辑实现只需要在UserRestApiHandle接口的实现类中去具体实现即可。

    Controller类,UserRestApiHandle 接口,java模型,前台ts模型均是根据yaml文件自动生成。如此以来rest接口维护就只需要关注yaml文件,以及UserRestApiHandle 实现类里面的逻辑就可以了。

    1.maven依赖:

    <dependencies>
        <dependency>
          <groupId>io.swagger</groupId>
          <artifactId>swagger-codegen</artifactId>
          <version>2.1.5</version>
        </dependency>
        <dependency>
          <groupId>com.github.spullara.mustache.java</groupId>
          <artifactId>compiler</artifactId>
          <version>0.9.2</version>
        </dependency>

    2.mustache模板语法一搜一大把:https://www.cnblogs.com/DF-fzh/p/5979093.html

    模板解析的时候主要是两种,一种根据map的key获取对应的value,一种是根据对象的属性名获取相应的值

    3.获取yaml文件内容(读文件)转化成Swagger对象

    String info = FileUtils.readFile(filePath);
    //将yaml文件转化为Swagger对象
    Swagger swagger = new SwaggerParser().parse(info);

     4.

    package ni.jun.yang;
    
    import io.swagger.codegen.ClientOptInput;
    import io.swagger.codegen.ClientOpts;
    import io.swagger.models.Swagger;
    import io.swagger.parser.SwaggerParser;
    import ni.jun.yang.ApiCodegen;
    import ni.jun.yang.JavaServiceCodegen;
    import ni.jun.yang.Util.FileUtils;
    
    import java.io.*;
    
    public class SwaggerTest
    {
    
        public void Test(String filePath) throws IOException {
            String info = FileUtils.readFile(filePath);
    
            //将yaml文件转化为Swagger对象
            Swagger swagger = new SwaggerParser().parse(info);
    
            //JavaServiceCodegen继承JavaClientCodegen(存放类的信息,类型对应["integer", "Integer"]表等等),用于扩展一些自定义功能
            JavaServiceCodegen serviceCodegen = new JavaServiceCodegen();
            ClientOptInput input = new ClientOptInput().opts(new ClientOpts()).swagger(swagger);
            input.setConfig(serviceCodegen);
    
            ApiCodegen apiCodegen = new ApiCodegen();
            apiCodegen.opts(input).generate();
    
        }
    }
    
    
    import io.swagger.codegen.*;
    import io.swagger.codegen.languages.JavaClientCodegen;
    
    
    public class JavaServiceCodegen extends JavaClientCodegen
    {
        public JavaServiceCodegen()
        {
            apiPackage = "ni.jun.yang.api";
            modelPackage = "ni.jun.yang.api.bean";
            modelTemplateFiles.put("bean.mustache", ".java");
            apiTemplateFiles.put("servicerest.mustache", ".java");
        }
    }

    5.组装yaml信息解析模板文件,生成各类文件

    package ni.jun.yang;
    
    import com.samskivert.mustache.Mustache;
    import com.samskivert.mustache.Template;
    import io.swagger.codegen.DefaultGenerator;
    import io.swagger.models.Path;
    import io.swagger.models.parameters.Parameter;
    import io.swagger.models.parameters.PathParameter;
    import io.swagger.models.properties.Property;
    import io.swagger.models.properties.RefProperty;
    import ni.jun.yang.Util.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class ApiCodegen extends DefaultGenerator
    {
        public List<File> generate() {
            List <Map<String,Object>> infoList = new ArrayList<>();
            List <Map<String,String>> importList = new ArrayList<>();
            Map<String,Path> pathMap = swagger.getPaths();
            Info info = new Info();
            info.apiPackage = config.apiPackage();
            info.modelPackage = config.modelPackage();
            info.basePath = swagger.getBasePath();
            info.className = swagger.getTags().get(0).getName();
    
            for (Map.Entry<String,Path> entry : pathMap.entrySet())
            {
                Map<String,Object> infoMap =  new HashMap<>();
                infoMap.put("urlName", entry.getKey());
                Path path = entry.getValue();
                changeType(path,infoMap,importList);
                infoMap.put("path",path);
                infoList.add(infoMap);
            }
            info.infoList = infoList;
            info.importList = importList;
            String outputFilePath = "src/main/java/ni/jun/yang/api/" + info.className + ".java";
            String templateFilePath = "src/main/resources/servicerest.mustache";
            String templateFileInfo = "";
            try {
                //获取模板信息
                templateFileInfo = FileUtils.readFile(templateFilePath);
                //生成模板
                Template template = Mustache.compiler().compile(templateFileInfo);
                //解析模板
                String result = template.execute(info);
                //生成Controller文件
                FileUtils.writeFile(result, outputFilePath);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    
            return null;
        }
    
        private void changeType(Path path, Map<String,Object> infoMap, List <Map<String,String>> importList)
        {
            List<Parameter> parameterList;
            Map<String, String> typeMap = config.typeMapping();
            if (path.getGet() != null)
            {
                infoMap.put("hasGet", true);
                parameterList = path.getGet().getParameters();
                for (Parameter parameter : parameterList)
                {
                    PathParameter pathParameter = (PathParameter)parameter;
                    pathParameter.setType(typeMap.get(pathParameter.getType()));
                }
                Property property = path.getGet().getResponses().get("200").getSchema();
                if (property != null)
                {
                    RefProperty refProperty = (RefProperty)property;
                    infoMap.put("responseType", refProperty.getSimpleRef());
                    Map<String,String> map = new HashMap<>();
                    map.put("import",config.modelPackage() + "." + refProperty.getSimpleRef());
                    importList.add(map);
                }
    
            }
            //TODO 其他几种请求 put,post,delete...
    
        }
    
        class Info
        {
            public String apiPackage;
            public String modelPackage;
            public String basePath;
            public String className;
            public List <Map<String,String>> importList;
            public List <Map<String,Object>> infoList;
        }
    }

    6.mustcahe模板文件:

    package {{apiPackage}};
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    {{#importList}}
    import {{{import}}};
    {{/importList}}
    
    @RestController
    public class {{className}}
    {
         //接口注入,逻辑均在实现类中实现
        {{className}}Handle handle;
    {{#infoList}}
    {{#hasGet}}
        @RequestMapping(value = "{{urlName}}",method = RequestMethod.GET)
        public {{responseType}} {{path.get.operationId}}(HttpServletRequest request{{#path.get.parameters}}, @PathVariable {{type}} {{name}}{{/path.get.parameters}}) {
            //TODO
            return handle.{{path.get.operationId}}(request{{#path.get.parameters}}, {{name}}{{/path.get.parameters}});
        }
     {{/hasGet}}
    {{/infoList}}
    }

    7.总结:使用Swagger结合mustache模板生成后台接口代码、以及前后台建模代码大致流程就这样,这里只贴出了生成Controller的相关代码,生成前后台模型也是根据自己的需求来重新组装yaml解析之后的definitions信息即可。

    重点是要知道模板解析的时候两种获取值的方式:通过类属性获取和根据key获取value,就可以根据自己需求来组装传入模板解析时候对象。

    最终要使用这些代码最好的还是做成maven插件(https://www.cnblogs.com/wangxinblog/p/8654400.html),编译即可生成相关的jar包。

    8.swaager yaml文件中自带的标签可能不能满足我们的需求,我们需要扩展一些标签,扩展标签要以x-开头,这些信息会放入到swagger对象的vendorExtensions属性下。如 x-abc: aaa

    有些人私信我,这里有个项目可以直接运行的,里面有Test直接运行就可以了,,可以生成controller文件,service的接口,还有对应的实体bean的文件
    链接:https://pan.baidu.com/s/1X3vt2yebe2Xin8qKFslHQA
    提取码:04wc

  • 相关阅读:
    填坑总结:python内存泄漏排查小技巧
    springMVC注解中@RequestMapping中常用参数value params 以及@RequestParam 详解
    springMVC 自定义类型转换器
    为什么Java需要lambda 表达式? 上帝爱吃苹果
    利器| Cypress 强大全新的 Web UI 测试框架应用尝鲜
    缺少锻炼面试的机会?城市群之北上广杭一起来了!
    实战 | 基于JMeter 完成典型电商场景(首页浏览)的性能压测
    一文搞定 pytest 自动化测试框架(一)
    测试面试 | Java 经典面试题汇总
    软件测试工程师成长痛点和职业发展建议
  • 原文地址:https://www.cnblogs.com/nijunyang/p/9313980.html
Copyright © 2011-2022 走看看