1. 程式人生 > >Swagger結合mustache模板生成後臺接口代碼、以及前後臺建模代碼

Swagger結合mustache模板生成後臺接口代碼、以及前後臺建模代碼

erl null enum div tco enc property 取模 javax

之前項目中使用的的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: "[email protected]"
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

技術分享圖片

技術分享圖片

Swagger結合mustache模板生成後臺接口代碼、以及前後臺建模代碼