一、背景
关于swagger的特性功能,用过的人都很清楚,确实也好用。可以参考http://springfox.github.io/springfox/docs/current/。另外关于swagger的使用集成,其实比较简单,网上博客也不少。尤其是SpringBoot出现后,集成更为简单方便。当然有时候也会遇到一些问题,写这篇就是记录了遇到的一些问题。其实以前集成过几次,都很顺手,但这次却遇到麻烦。
二、问题
这次swagger的集成,是在原有项目的基础上进行的。开始是打开不了页面,后面能打开了却出不来接口的内容。换过swagger的版本,但仍然不行,网上的资料也看过,还是不能解决。尝试了几次之后,终于成功!过程中遇到的问题主要有以下几类:
- swagger-ui.html页面打不开;
- swagger-ui.html页面打开了,但接口显示出不来;
- swagger-resource/configuration/ui报404错误;
- 当swagger的依赖切换到2.8相对高一点的版本时,打开swagger-ui.html时会提示弹窗,关了还会弹出;
- 其它问题,不记得了。
其中比较接近成功的一次是出现了下面的页面(下面的图经过处理了,有点模糊),只是接口的信息没有列出来。
三、问题分析与解决
3.1 问题分析
在分析问题时,肯定是要结合项目自身的情况。要清楚项目用了哪些框架,这些框架结合时,哪些地方可能会有影响等。也要清楚swagger本身的一些信息,知道它的结构目录、大概原理等。当然遇到简单的问题的话,可以不需要知道这些,但这次解决确实是分析了几方面的因素。
先介绍下项目本身的情况,项目是分布式的,已经上线运行了一些时间。使用的框架大概如下:
- Spring MVC;
- Shiro;
- 相关联的使用了Hibernate、Mybatis、Spring等等。
而我需要集成swagger的项目,主要使用了Spring MVC和shiro,其它的没有影响。Spring MVC都熟悉,shiro是安全方面的框架。清楚这些情况后,开始分析出现问题的可能原因:
- web.xml配置的原因,拦截了swagger-ui.html及相关请求;
- Spring MVC配置文件的原因,比如没有扫描到相应的Controller;
- shiro的原因,因为shiro也拦截一些没有验证或没有放开(放开就是可以直接访问的如js文件)的请求;
- 项目使用了过滤器的原因,也将请求拦截了;
- 其它原因。
经过一定程序的熟悉了解,清楚了项目确实有过滤器,会将请求先进行拦截。而我们在集成swagger后访问的地址是http://ip:port/projectName/swagger-ui.html。如果在web.xml中配置的请求是*.json,那这个请求就进不去,也就访问不了了。当时的配置如下所示(自然是访问不了的):
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>
请求进入过滤器后,对很多请求进行拦截验证,所以这样一来,就算我让swager-ui.html请求通过,但请求swagger-ui.html后swagger的其它请求依然不通过,因为简单的让使用swagger时出现的请求通过,不能从根本上解决问题。
对使用swagger时,一些请求及静态文件也会被shiro拦截,所以也需要修改shiro的配置。
对swagger,要确保它本身请求对应的swagger中的控制器能被扫描到。同时也要在项目中开启swagger的使用。下面开始解决问题。
3.2 问题解决
3.2.1 解决方案
我当时的解决方案是,只引入springfox-swagger2依赖而不引入springfox-swagger-ui依赖,将springfox-swagger-ui架包中的前端内容拷贝到我们的项目中,然后写控制器来引导swagger的访问。因为springfox-swagger-ui依赖的内容就是前端的内容,没有Java的代码,它请求的是springfox-swagger2。
首先说下使用swagger使用的依赖,其中我用的版本为2.6.1。然后开始修改工作。
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
3.2.2 修改配置文件:修改web.xml和Spring MVC的配置文件。
先修改web.xml文件,如下:
<!-- 使用swagger时,注释下面的filter,不然swagger的请求会被拦截而不能通过 -->
<!--<filter>-->
<!--<filter-name>URL-REDIRECT-FILTER</filter-name>-->
<!--<filter-class>com.corn.web.filters.UrlAuthAndEncryFilter</filter-class>-->
<!--</filter>-->
<!--<filter-mapping>-->
<!--<filter-name>URL-REDIRECT-FILTER</filter-name>-->
<!--<url-pattern>/*</url-pattern>-->
<!--</filter-mapping>-->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 启用swagger时,用此配置 -->
<!--<url-pattern>/</url-pattern>-->
<!-- 线上环境时,不启用swagger,用下面的配置 -->
<url-pattern>*.json</url-pattern>
</servlet-mapping>
修改web.xml文件配置的目的在于取消项目对swagger的请求拦截,修改以后好让swagger的请求能够顺利通过。
再来修改Spring MVC的配置文件,需要加入swagger中controller所在包的路径扫描这一配置,如下:
<!-- 在启用swagger时,因为需要请求swagger的Controller,所以需要把swagger请求的Controller对就的路径springfox.documentation.swagger2.web加入扫描中 -->
<context:component-scan base-package="springfox.documentation.swagger2.web" />
为什么路径是springfox.documentation.swagger2.web?以下面的截图就知道了。这是springfox-swagger2的架包,swagger的请求主要对应在Swagger2Controller这个类。
另外,需要注意的是,路径springfox.documentation.swagger2.web虽然包含在springfox这中,但不要只写springfox,因为只写springfox,它扫描的文件就多了,做了一些不必要的事。虽然不影响结果。当时我只写了springfox,结果打开swagger-ui.html到接口显示出来,尤其的慢,编辑器的控制台也在不停的刷,所以其中肯定是有其它的操作的。
3.2.3 改造springfox-swagger-ui
改造后的项目结构如下图所示:
其中,/static/swagger目录下的内容,就是springfox-swagger-ui依赖的文件。具体如下图:
原来的swagger-ui.html文件,我放在了/WEB-INF/pages/swagger目录下。这样springfox-swagger-ui移植到本地的初步改造完成(其中swagger-ui.html文件没有用到,最初复制过来没有删除,o2c.jsp也没有用到)。其中index.jsp是对原有的swagger-ui.html进行了一些修改,代码如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" pageEncoding="UTF-8"%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="webjars/springfox-swagger-ui/images/favicon-32x32.png" sizes="32x32"/>
<link rel="icon" type="image/png" href="webjars/springfox-swagger-ui/images/favicon-16x16.png" sizes="16x16"/>
<link href='${ctx}/static/swagger/webjars/springfox-swagger-ui/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='${ctx}/static/swagger/webjars/springfox-swagger-ui/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='${ctx}/static/swagger/webjars/springfox-swagger-ui/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='${ctx}/static/swagger/webjars/springfox-swagger-ui/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='${ctx}/static/swagger/webjars/springfox-swagger-ui/css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/object-assign-pollyfill.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/handlebars-4.0.5.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/lodash.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/backbone-min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/swagger-ui.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/jsoneditor.min.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/marked.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lib/swagger-oauth.js' type='text/javascript'></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/springfox.js' type='text/javascript'></script>
<!-- 加入下面的两个js后,访问swagger时语言会切换到中文 -->
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lang/translator.js' type="text/javascript"></script>
<script src='${ctx}/static/swagger/webjars/springfox-swagger-ui/lang/zh-cn.js' type="text/javascript"></script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">
<img class="logo__img" alt="swagger" height="30" width="30" src="${ctx}/static/swagger/webjars/springfox-swagger-ui/images/logo_small.png" />
<span class="logo__title">swagger</span>
</a>
<form id='api_selector'>
<div class='input'>
<select id="select_baseUrl" name="select_baseUrl"/>
</div>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div id='auth_container'></div>
<div class='input'><a id="explore" class="header__btn" href="#" data-sw-translate>Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate> </div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
下面开始编辑控制器,代码如下,SwaggerController.java主要是用来引导swagger-ui.html的访问,请求后会进入/WEB-INF/pages/swagger/index.jsp,而不能直接访问html页面,因为在Spring MVC的配置文件定义了页面的后缀为.jsp,这是项目本身的需要。
public class SwaggerController {
/**
* @Description 进入Swagger-ui.html页面
* @Author Ethan
* @Param []
* @return org.springframework.web.servlet.ModelAndView
* @Version 1.0
*/
@RequestMapping("/swagger-ui")
public ModelAndView index() {
ModelAndView mv = new ModelAndView();
mv.setViewName("swagger/index");
return mv;
}
// @RequestMapping(value = "/webjars/springfox-swagger-ui/o2c", method = RequestMethod.GET)
// public ModelAndView o2c() {
// ModelAndView mv = new ModelAndView();
// mv.setViewName("swagger/o2c");
// return mv;
// }
}
3.2.3 添加Swagger的配置类
swagger的配置类如下,其实很简单。
@Configuration
@EnableWebMvc
@EnableSwagger2
@ComponentScan(basePackages = {"com.corn.web.controller"})
public class SwaggerConfig {
}
到此,swagger的集成与本地化改造已经完成,这是我解决swagger集成问题的一种方法。启动项目后,开始访问swagger。
输入的地址,是http://localhost:8088/swagger-ui.html,后面可能会慢一点,下面截图中,提示信息是正常的,可能几秒钟后会消失,接口正常显示。