最近找了一个自动生成api文档的工具swagger,相对swaggerEdit就不说了。个人比较懒,还是自动生成来的便捷,尤其是老项目,新项目在初期可能会维护,但是到了后期就很难保证了。所以,那些需要一些特殊配置说明的文档工具就不提了。
这篇文章主要是在swagger2 swagger UI的基础上结合nginx解决跨域来实现统一的api文档中心,至于如何搭建swagger2请自行百度。
使用swagger2生成离线文档比较麻烦,尤其是非springcloud项目(具体实现方式,请自行百度)。本文另辟蹊径,通过修改swagger部分js脚本文件、提供统一的资源加载路径、使用nginx反向代理解决swagger测试跨域问题,来搭建一个统一的api文档中心系统。
首先说一下swagger的jar包:
其他自行百度,主要说一下swagger的请求流程:
swaggerUI的静态文件:
springfox-swagger-ui-2.6.1.jar 解压缩打开后在META-INF\resources下面就可以看到我们访问的html页以及静态文件。
swagger2的几个资源服务controller:
swagger-resources//configuration/security
swagger-resources/configuration/ui:控制页面展示效果
swagger-resources/:比较重要,这个也是我们要覆盖重写的主要协议
所在jar包:springfox-swagger-common-2.6.1.jar
controller类路径: springfox.documentation.swagger.web.ApiResourceController
最重要的代码路径,生成api文档的服务:
jar包:springfox-swagger2-2.6.1.jar
controller类路径:springfox.documentation.swagger2.web.Swagger2Controller
我们要搭建中心api文档,或者离线文档,那就需要更改js文件,把资源路径修改为统一的服务路径或者静态json(访问/v2/api-docs可以获取接口描述json)文件。
贴代码:重写swagger-resources服务和提供统一的api-docs文档(swagger-resources提供的location就是生成接口描述页面数据的来源访问路径):
@RequestMapping("my")
@RestController
public class SwaggerController {
private static Logger logger = LoggerFactory.getLogger(SwaggerController.class);
private static Map<String,SwaggerResource> resourceMap = new HashMap<String,SwaggerResource>();
public static void getDocJson(){
String filename = "apidoc.json";
Type type =new TypeToken<Map<String,SwaggerResource>>(){}.getType();
resourceMap = new Gson().fromJson(
new InputStreamReader(SwaggerController.class.getClassLoader().getResourceAsStream(filename),Charset.forName("UTF-8")),type);
}
static{
getDocJson();
}
@RequestMapping("/swagger-resources")
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
getDocJson();
List<SwaggerResource> swaggerResources = new ArrayList<SwaggerResource>();
if(resourceMap!=null&&resourceMap.size()>0){
for (String key : resourceMap.keySet()) {
SwaggerResource resource = resourceMap.get(key);
swaggerResources.add(resource);
if(StringUtils.isBlank(resource.getLocation())){
resource.setLocation("/my/apidoc/"+key);
}
}
}
/*SwaggerResource resource = new SwaggerResource();
resource.setName("default");
resource.setLocation("/v2/api-docs");
resource.setSwaggerVersion("2.0");
swaggerResources.add(resource);
resource = new SwaggerResource();
resource.setName("本地文件");
resource.setLocation("/apidoc/Goods_0.0.1.json");
resource.setSwaggerVersion("0.0.1");
swaggerResources.add(resource);
resource = new SwaggerResource();
resource.setName("远程测试");
resource.setLocation("http://127.0.0.1/Goods_0.0.1.json");
resource.setSwaggerVersion("0.0.1");
swaggerResources.add(resource);*/
return new ResponseEntity<List<SwaggerResource>>(swaggerResources, HttpStatus.OK);
}
@RequestMapping("/apidoc/{id}")
public String findJsonDocs1(@PathVariable("id")String id) {
SwaggerResource resource = resourceMap.get(id);
String swagger = "";
if(StringUtils.isNotBlank(resource.getPath())){
RestTemplate template = new RestTemplate();
ResponseEntity<String> body = template.exchange(resource.getPath(), HttpMethod.GET, null, String.class);
try {
return new String(body.getBody().getBytes("iso8859-1"),"utf-8");
} catch (UnsupportedEncodingException e) {
logger.error("远程获取数据转码异常(findJsonDocs1):", e);
}
}else if(StringUtils.isNotBlank(resource.getFileName())){
try {
InputStreamReader in = new InputStreamReader(SwaggerController.class.getClassLoader().getResourceAsStream(resource.getFileName()),Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(in);
String tmp = null;
StringBuilder sb = new StringBuilder();
while((tmp = reader.readLine()) != null){
sb.append(tmp);
}
swagger = sb.toString();
} catch (IOException e) {
logger.error("读取文件异常(findJsonDocs1):", e);
}
return swagger;
}
return JSONObject.toJSONString( new Swagger());
}
}
提供的配置文件:apidoc.json
{
"1":{"name":"default","location":"/v2/api-docs","swaggerVersion":"2.0","host":"http://localhost:8080/","path":"","fileName":""},
"2":{"name":"本地文件","location":"","swaggerVersion":"2.0","host":"http://localhost:8080","path":"","fileName":"Goods_0.0.1.json"},
"3":{"name":"远程文件","location":"","swaggerVersion":"2.0","host":"http://127.0.0.1","path":"http://127.0.0.1/Goods_0.0.1.json","fileName":""}
}
其中,path表示远程http协议路径,fileName表示本地资源文件路径。
对应的Bean实体类:
public class SwaggerResource extends springfox.documentation.swagger.web.SwaggerResource{
/**
* 域名端口
*/
private String host;
/**
* 远程资源路径
*/
private String path;
/**
* 本地资源
*/
private String fileName;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
贴一下改写的js文件:springfox.js:
function initializeBaseUrl() {
var relativeLocation = springfox.baseUrl();
$('#input_baseUrl').hide();
$.getJSON(relativeLocation + "/my/swagger-resources", function(data) {
var $urlDropdown = $('#select_baseUrl');
$urlDropdown.empty();
$.each(data, function(i, resource) {
var option = $('<option></option>')
.attr("value", maybePrefix(resource.location, relativeLocation))
.text(resource.name + " (" + resource.location + ")");
$urlDropdown.append(option);
});
$urlDropdown.change();
});
}
这是springfox.js的最后一个函数(initializeBaseUrl),如果是搭建中心的api文档系统,更改/swagger-resources路径为我们自定义的服务路径/my/swagger-resources即可。
如果是搭建离线的文档,讲这部分代码改写成访问静态资源文件就可以了。
/swagger-resources数据格式:
[{"name":"default","location":"/v2/api-docs","swaggerVersion":"2.0"}]
效果图: