springMvc,springFox 2.6.1 ,swagger-ui:2.6.1,用idea启动后(自动放入tomcat容器里),现象:
F12查看:
http://localhost:8080/agent_applications_war/swagger-resources
此接口返回值不对。首先说明一下,该接口应该返回的是swagger2提供的api地址,但实际却返回了非法字符串${springfox.documentation.swagger.v2.path:/v2/api-docs}。我第一时间在配置文件里加入该配置,但是还是不行,所以,到控制台找到出错的接口:
ApiResourceController (负责提供资源):
这个类自动注入时,容器给他提供了一个SwaggerResourcesProvider:
然后,我的思路是,真实的swaggerApi的接口到底启动没有呢?我直接访问/v2/api-doc测试:
Swagger2Controller是swagger提供的api接口,它通过扫描注解,缓存我们的接口信息,然后给swagger-ui调用:
开启断点:
所以,初步找到问题了:
SwaggerResourcesProvider初始化错误,url不对,
Swagger2Controller初始化不对,导致它根本没起来。
解决方案:
1,自己写Swagger2Controller接口:MySwagger2Controller,把原来的拷贝过来即可;
2,自己写一个资源提供接口,返回我们MySwagger2Controller的访问路径;
3,自己写一个接口,转发/swagger-resources接口,注意,是只转发它,并不是转发它下面所以的接口。转发到上一步那个方法里。通过filter实现。
代码结构:
代码:
package com.ygkj.agent.server.action.mySwagger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.swagger.web.SwaggerResource;
/**
* MyApiResourceController
* 提供给swagger-ui调用,告诉它获取api列表数据的url
* 责任人: Chuck
* 修改人: Chuck
* 创建/修改时间: 2020/5/14 15:49
* Copyright : 2014-2018 深圳令令科技有限公司-版权所有
**/
@Controller
@ApiIgnore
@RequestMapping({"/swagger-resources2"})//原来的swagger-resources会被重新分配至这里
public class MyApiResourceController {
@RequestMapping( method = {RequestMethod.GET})
@ResponseBody
ResponseEntity<List<SwaggerResource>> swaggerResources() {
List<SwaggerResource> result=new ArrayList<>();
SwaggerResource r1=new SwaggerResource();
r1.setName("default");
//重置url,返回我们自己写的swaggerApi列表接口 chuck:2020/05/14
r1.setLocation("/swagger/v2/api-docs");//核心是这里,此次出现问题,是由于springmvc里注解出现异常,
// 导致api路径出问题,最终导致刷不出api列表
r1.setSwaggerVersion("2.0");
result.add(r1);
return new ResponseEntity(result, HttpStatus.OK);
}
}
package com.ygkj.agent.server.action.mySwagger;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import io.swagger.models.Swagger;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.service.Documentation;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
/**
* MySwagger2Controller
* 这个类,拷贝自swagger2. 功能一致,只是改了访问路径
* 责任人: Chuck
* 修改人: Chuck
* 创建/修改时间: 2020/5/14 15:32
* Copyright : 2014-2018 深圳令令科技有限公司-版权所有
**/
@Controller
@ApiIgnore
@RequestMapping("/swagger")
public class MySwagger2Controller {
public static final String DEFAULT_URL = "/v2/api-docs";
private static final String HAL_MEDIA_TYPE = "application/hal+json";
private String hostNameOverride="DEFAULT";
@Autowired
private DocumentationCache documentationCache;
@Autowired
private ServiceModelToSwagger2Mapper mapper;
@Autowired
private JsonSerializer jsonSerializer;
@ApiIgnore
@RequestMapping(
value = {"/v2/api-docs"},
method = {RequestMethod.GET},
produces = {"application/json", "application/hal+json"}
)
@ResponseBody
public ResponseEntity<Json> getDocumentation(@RequestParam(value = "group",required = false) String swaggerGroup, HttpServletRequest servletRequest) {
String groupName = (String) Optional.fromNullable(swaggerGroup).or("default");
Documentation documentation = this.documentationCache.documentationByGroup(groupName);
if (documentation == null) {
return new ResponseEntity(HttpStatus.NOT_FOUND);
} else {
Swagger swagger = this.mapper.mapDocumentation(documentation);
if (Strings.isNullOrEmpty(swagger.getHost())) {
UriComponents uriComponents = componentsFrom(servletRequest);
swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
swagger.host(this.hostName(uriComponents));
}
return new ResponseEntity(this.jsonSerializer.toJson(swagger), HttpStatus.OK);
}
}
private String hostName(UriComponents uriComponents) {
if ("DEFAULT".equals(this.hostNameOverride)) {
String host = uriComponents.getHost();
int port = uriComponents.getPort();
return port > -1 ? String.format("%s:%d", host, port) : host;
} else {
return this.hostNameOverride;
}
}
static UriComponents componentsFrom(HttpServletRequest request) {
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromServletMapping(request);
ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader(ForwardedHeader.NAME));
String proto = StringUtils.hasText(forwarded.getProto()) ? forwarded.getProto() : request.getHeader("X-Forwarded-Proto");
String forwardedSsl = request.getHeader("X-Forwarded-Ssl");
if (StringUtils.hasText(proto)) {
builder.scheme(proto);
} else if (StringUtils.hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on")) {
builder.scheme("https");
}
String host = forwarded.getHost();
host = StringUtils.hasText(host) ? host : request.getHeader("X-Forwarded-Host");
if (!StringUtils.hasText(host)) {
return builder.build();
} else {
String[] hosts = StringUtils.commaDelimitedListToStringArray(host);
String hostToUse = hosts[0];
if (hostToUse.contains(":")) {
String[] hostAndPort = StringUtils.split(hostToUse, ":");
builder.host(hostAndPort[0]);
builder.port(Integer.parseInt(hostAndPort[1]));
} else {
builder.host(hostToUse);
builder.port(-1);
}
String port = request.getHeader("X-Forwarded-Port");
if (StringUtils.hasText(port)) {
builder.port(Integer.parseInt(port));
}
return builder.build();
}
}
static class ForwardedHeader {
public static String NAME = "Forwarded";
private static final ForwardedHeader NO_HEADER = new ForwardedHeader(Collections.emptyMap());
private final Map<String, String> elements;
private ForwardedHeader(Map<String, String> elements) {
this.elements = elements;
}
public static ForwardedHeader of(String source) {
if (!StringUtils.hasText(source)) {
return NO_HEADER;
} else {
Map<String, String> elements = new HashMap();
String[] var2 = source.split(";");
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
String part = var2[var4];
String[] keyValue = part.split("=");
if (keyValue.length == 2) {
elements.put(keyValue[0].trim(), keyValue[1].trim());
}
}
Assert.notNull(elements, "Forwarded elements must not be null!");
Assert.isTrue(!elements.isEmpty(), "At least one forwarded element needs to be present!");
return new ForwardedHeader(elements);
}
}
public String getProto() {
return (String)this.elements.get("proto");
}
public String getHost() {
return (String)this.elements.get("host");
}
}
}
spring.mvc.xml(拦截器配置,对swagger的请求直接放行):
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/v2/**"/>
<mvc:exclude-mapping path="/swagger-resources/**"/>
<mvc:exclude-mapping path="/swagger-resources2/**"/>
<mvc:exclude-mapping path="/swagger/**"/>
<mvc:exclude-mapping path="/webjars/**"/>
<mvc:exclude-mapping path="/swagger-ui.html/**"/>
<mvc:exclude-mapping path="/agent/account/login"/>
<mvc:exclude-mapping path="/agent/account/verify/code"/>
<bean class="com.ygkj.agent.server.web.AccessAllowInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
过滤器(通过过滤器,转发出问题的那个接口,把业务转到我们自己写的接口里来):
package com.ygkj.agent.server.web;
/**
* SwaggerFilter
* 责任人: Chuck
* 修改人: Chuck
* 创建/修改时间: 2020/5/14 16:06
* Copyright : 2014-2018 深圳令令科技有限公司-版权所有
**/
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
/**
* SwaggerFilter
* 整个项目swagger集成失败的原因是:
* 原框架里的Swagger2Controller 和 ApiResourceController里的SwaggerResourcesProvider
* 的 @Value("${springfox.documentation.swagger.v2.path:/v2/api-docs}")
* private String swagger2Url;
* 这个注解加载失败。
* 所以,解决方案是:
* 1,转发获取swagger2Url的请求,使其调用我们自己的接口
* 2,自己拷贝swagger的接口列表业务,业务和原来一致,只是访问路径不同
* 责任人: Chuck
* 修改人: Chuck
* 创建/修改时间: 2020/5/14 16:26
* @version 1.0.0
* Copyright : 2014-2017 深圳令令科技有限公司-版权所有
**/
public class SwaggerFilter implements Filter
{
public void destroy()
{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse)response;
String url=req.getRequestURI();
log.info("url:{}",url);
String replace="/swagger-resources2";
String myApiResourceController=url.replace("/swagger-resources",replace);
if( url.endsWith("/swagger-resources"))//如果是这个,则把它转发到我们自己写的MyApiResourceController
{
res.sendRedirect(myApiResourceController);
}
else
filterChain.doFilter(req, res);
}
public void init(FilterConfig arg0) throws ServletException
{
}
}
web.xml(通过过滤器,转发出问题的那个接口,把业务转到我们自己写的接口里来):
<filter>
<filter-name>SwaggerFilter</filter-name>
<filter-class>com.ygkj.agent.server.web.SwaggerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SwaggerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
最终效果,输入:http://localhost:8080/agent_applications_war/swagger-ui.html:
感谢老秦提供解决方案的思路,博客地址:
https://blog.csdn.net/qinxian20120