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