springboot与swagger与zuul整合


title: springboot与swagger与zuul整合
copyright: true
categories: springmvc和springboot
tags: swagger
password:


  • 1、项目结构:
    涉及到的服务有:
    api-gateway、userservice、taskservice,项目由Springboot-springcloud-jpa-fegin-consul-zuul构成微服务
  • 2、网关服务:
    其中 api-gateway 是网关服务 ,负责url的分发到各个微服务,用的是zuul,通过url匹配,在consul中找到并分发到不同服务,服务注册发现容器用的是consul,还可以用于配置项目key-value
    consul配置如下:
spring: 
cloud: 
inetutils: 
ignoredInterfaces: 
- docker0 
- veth.* 
- VMware.* 
- VPN.* 
consul: 
host: consul.zhibi.config 
#port: 8500 
discovery: 
healthCheckInterval: 15s #服务中心健康检查间隔 
preferIpAddress: true#使用外部IP注册服务,默认使用的是主机名 
health-check-critical-timeout: 5m #5分钟没有恢复服务的从服务注册中心移除 
instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${server.port} 
config: 
prefix: config 
defaultContext: xiuba-apigateway 
profileSeparator: ',' 
format: PROPERTIES 
 

服务分发用的是cloud全家桶中的zuul服务
配置如下:


spring: 
   application: 
      name: apigateway 
zuul: 
     ignoredServices: '*' 
    routes: 
         user-api: 
             path: /user/** 
           stripPrefix: false 
           serviceId: userservice 
        task-api: 
          path: /task/** 
           stripPrefix: false 
           serviceId: taskservice 
      debug: 
           request: true 
           include-debug-header: true 
 
      host: 
          connect-timeout-millis: 10000 
          socket-timeout-millis: 60000 
          max-total-connections: 1000 
         max-per-route-connections: 200 
      ribbon: 
          ReadTimeout: 10000 
          #开启饥饿模式,启动时创建好对应服务的client 
          eager-load: 
                 enabled: true 
          clients: userservice,taskservice,orderservice,payservice,searchservice 

以/user开始的url会被分配到userservice服务中,

以/task开始的url会被分派到taskservice服务中,

stripPrefix: true的时候,访问相应服务的时候会去掉前面的/user、/task前缀

  • 3、task、user服务
    cloud配置:
spring: 
cloud: 
inetutils: 
ignoredInterfaces: 
- docker0 
- veth.* 
- VMware.* 
- VPN.* 
consul: 
 host: consul.zhibi.config 
 #port: 8500 

 
discovery: 
 healthCheckInterval: 15s #服务中心健康检查间隔 
 
preferIpAddress: true#使用外部IP注册服务,默认使用的是主机名 
 
health-check-critical-timeout: 5m #5分钟没有恢复服务的从服务注册中心移除 
 
instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${server.port} 
 config: 
 prefix: config 
 defaultContext: xiuba-task 
 profileSeparator: ',' 
 format: PROPERTIES 
 
  • 4、swagger的maven依赖如下:
<dependency> 
    <groupId>io.springfox</groupId> 
    <artifactId>springfox-swagger2</artifactId> 
    <version>RELEASE</version> 
</dependency>
 
<dependency> 
    <groupId>io.springfox</groupId> 
    <artifactId>springfox-swagger-ui</artifactId> 
    <version>RELEASE</version> 
</dependency> 
 
  • 5、zuul-swagger配置

①、SwaggerConfig.java:

package com.zhibi.xiuba.config; 
 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import springfox.documentation.builders.ApiInfoBuilder; 
import springfox.documentation.service.ApiInfo; 
import springfox.documentation.service.Contact; 
import springfox.documentation.spi.DocumentationType; 
import springfox.documentation.spring.web.plugins.Docket; 
import springfox.documentation.swagger.web.UiConfiguration; 
import springfox.documentation.swagger2.annotations.EnableSwagger2; 
 
/** 
* Created by qinhe_admin on 2017/9/20. 
*/ 
@Configuration 
@EnableSwagger2 
public class SwaggerConfig { 
 
@Bean 
public Docket createRestApi() { 
return new Docket(DocumentationType.SWAGGER_2) 
.apiInfo(apiInfo()); 
} 
 
private ApiInfo apiInfo() { 
return new ApiInfoBuilder() 
.title("白拿拿系统") 
.description("白拿拿系统接口文档说明") 
.termsOfServiceUrl("http://www.xiuba365.com") 
.contact(new Contact("秦贺", "", "qinhelili@gmail.com")) 
.version("1.0") 
.build(); 
} 
 
@Bean 
UiConfiguration uiConfig() { 
return new UiConfiguration(null, "list", "alpha", "schema", 
UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true); 
} 
} 
 

②、DocumentationConfig


package com.zhibi.xiuba.config; 
 
import org.springframework.context.annotation.Primary; 
import org.springframework.stereotype.Component; 
import springfox.documentation.swagger.web.SwaggerResource; 
import springfox.documentation.swagger.web.SwaggerResourcesProvider; 
 
import java.util.ArrayList; 
import java.util.List; 
 
/** 
* Created by qinhe_admin on 2017/9/20. 
*/ 
@Component 
@Primary 
public class DocumentationConfig implements SwaggerResourcesProvider { 
@Override 
public List<SwaggerResource> get() { 
List resources = new ArrayList<>(); 
resources.add(swaggerResource("活动task模块API", "/task/v2/api-docs", "2.0")); 
resources.add(swaggerResource("用户账户user模块API", "/user/v2/api-docs", "2.0")); 
return resources; 
} 
 
private SwaggerResource swaggerResource(String name, String location, String version) { 
SwaggerResource swaggerResource = new SwaggerResource(); 
swaggerResource.setName(name); 
swaggerResource.setLocation(location); 
swaggerResource.setSwaggerVersion(version); 
return swaggerResource; 
} 
} 
 
  • 6、task和user服务的swagger配置

配置:


    xiuba: 
    swagger: 
    title: 活动task模块API 
    description: 活动模块接口文档说明 
    termsOfServiceUrl: http://www.xiuba365.com/ 
    version: 1.0 
    basePackage: com.zhibi.xiuba.controller 
    module: task 
    xiuba: 
    swagger: 
    title: 用户账户user模块API 
    description: 用户账户模块接口文档说明 
    termsOfServiceUrl: http://www.xiuba365.com/ 
    version: 1.0 
    basePackage: com.zhibi.xiuba.controller 
    module: user 

SwaggerConfig.java:

package com.zhibi.xiuba.configers; 
 
import org.springframework.boot.context.properties.ConfigurationProperties; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import springfox.documentation.builders.ApiInfoBuilder; 
import springfox.documentation.builders.PathSelectors; 
import springfox.documentation.builders.RequestHandlerSelectors; 
import springfox.documentation.service.ApiInfo; 
import springfox.documentation.service.Contact; 
import springfox.documentation.spi.DocumentationType; 
import springfox.documentation.spring.web.plugins.Docket; 
import springfox.documentation.swagger.web.UiConfiguration; 
 
/** 
* Created by qinhe_admin on 2017/9/20. 
*/ 
@Configuration 
@ConfigurationProperties(prefix = "xiuba.swagger") 
public class SwaggerConfig { 
 
private String title; 
 
private String description; 
 
private String termsOfServiceUrl; 
 
private String version; 
 
private String basePackage; 
 
public String getTitle() { 
return title; 
} 
 
public void setTitle(String title) { 
this.title = title; 
} 
 
public String getDescription() { 
return description; 
} 
 
public void setDescription(String description) { 
this.description = description; 
} 
 
public String getTermsOfServiceUrl() { 
return termsOfServiceUrl; 
} 
 
public void setTermsOfServiceUrl(String termsOfServiceUrl) { 
this.termsOfServiceUrl = termsOfServiceUrl; 
} 
 
public String getVersion() { 
return version; 
} 
 
public void setVersion(String version) { 
this.version = version; 
} 
 
public String getBasePackage() { 
return basePackage; 
} 
 
public void setBasePackage(String basePackage) { 
this.basePackage = basePackage; 
} 
 
@Bean 
public Docket createRestApi() { 
return new Docket(DocumentationType.SWAGGER_2) 
.apiInfo(apiInfo()) 
.select() 
.apis(RequestHandlerSelectors.basePackage(basePackage)) 
//                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) 
//                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) 
.paths(PathSelectors.any()) 
.build(); 
} 
 
private ApiInfo apiInfo() { 
return new ApiInfoBuilder() 
.title(title) 
.description(description) 
.termsOfServiceUrl(termsOfServiceUrl) 
.contact(new Contact("秦贺", "", "qinhelili@gmail.com")) 
.version(version) 
.build(); 
} 
 
@Bean 
UiConfiguration uiConfig() { 
return new UiConfiguration(null, "list", "alpha", "schema", UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true); 
} 
} 
 

修改源代码的url:

Swagger2Controller.java:

package com.zhibi.xiuba.configers; 
 
import com.google.common.base.Optional; 
import com.google.common.base.Strings; 
import io.swagger.models.Swagger; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.context.properties.ConfigurationProperties; 
import org.springframework.core.env.Environment; 
import org.springframework.http.HttpStatus; 
import org.springframework.http.ResponseEntity; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.*; 
import org.springframework.web.util.UriComponents; 
import springfox.documentation.annotations.ApiIgnore; 
import springfox.documentation.service.Documentation; 
import springfox.documentation.spring.web.DocumentationCache; 
import springfox.documentation.spring.web.PropertySourcedMapping; 
import springfox.documentation.spring.web.json.Json; 
import springfox.documentation.spring.web.json.JsonSerializer; 
import springfox.documentation.spring.web.plugins.Docket; 
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; 
 
import javax.servlet.http.HttpServletRequest; 
 
import static com.google.common.base.Strings.isNullOrEmpty; 
import static com.zhibi.xiuba.configers.HostNameProvider.componentsFrom; 
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE; 
 
@Controller 
@ApiIgnore 
@ConfigurationProperties(prefix = "xiuba.swagger") 
public class Swagger2Controller { 
 
private String module; 
private static final String HAL_MEDIA_TYPE = "application/hal+json"; 
 
private final String hostNameOverride; 
private final DocumentationCache documentationCache; 
private final ServiceModelToSwagger2Mapper mapper; 
private final JsonSerializer jsonSerializer; 
 
public String getModule() { 
return module; 
} 
 
public void setModule(String module) { 
this.module = module; 
} 
 
@Autowired 
public Swagger2Controller( 
Environment environment, 
DocumentationCache documentationCache, 
ServiceModelToSwagger2Mapper mapper, 
JsonSerializer jsonSerializer) { 
 
this.hostNameOverride = environment.getProperty("springfox.documentation.swagger.v2.host", "DEFAULT"); 
this.documentationCache = documentationCache; 
this.mapper = mapper; 
this.jsonSerializer = jsonSerializer; 
} 
 
@RequestMapping(value = "/{module}/v2/api-docs", method = RequestMethod.GET, produces = {APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE}) 
@PropertySourcedMapping( 
value = "${springfox.documentation.swagger.v2.path}", 
propertyKey = "springfox.documentation.swagger.v2.path") 
@ResponseBody 
public ResponseEntity<Json> getDocumentation( 
@RequestParam(value = "group", required = false) String swaggerGroup, 
@PathVariable(value = "module", required = true) String module, 
HttpServletRequest servletRequest) { 
 
if (org.apache.commons.lang3.StringUtils.isNotBlank(module) && module.equalsIgnoreCase(this.module)) { 
String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME); 
Documentation documentation = documentationCache.documentationByGroup(groupName); 
if (documentation == null) { 
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND); 
} 
Swagger swagger = mapper.mapDocumentation(documentation); 
UriComponents uriComponents = componentsFrom(servletRequest, swagger.getBasePath()); 
swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath()); 
if (isNullOrEmpty(swagger.getHost())) { 
swagger.host(hostName(uriComponents)); 
} 
return new ResponseEntity<Json>(jsonSerializer.toJson(swagger), HttpStatus.OK); 
}else { 
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND); 
} 
} 
 
private String hostName(UriComponents uriComponents) { 
if ("DEFAULT".equals(hostNameOverride)) { 
String host = uriComponents.getHost(); 
int port = uriComponents.getPort(); 
if (port > -1) { 
return String.format("%s:%d", host, port); 
} 
return host; 
} 
return hostNameOverride; 
} 
} 
 

HostNameProvider.java:

package com.zhibi.xiuba.configers; 
 
 
import org.springframework.http.server.ServletServerHttpRequest; 
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 
import org.springframework.web.util.UriComponents; 
import org.springframework.web.util.UriComponentsBuilder; 
import org.springframework.web.util.UrlPathHelper; 
 
import javax.servlet.http.HttpServletRequest; 
 
import static org.springframework.util.StringUtils.hasText; 
import static org.springframework.web.servlet.support.ServletUriComponentsBuilder.fromContextPath; 
 
public class HostNameProvider { 
 
public HostNameProvider() { 
throw new UnsupportedOperationException(); 
} 
 
static UriComponents componentsFrom( 
HttpServletRequest request, 
String basePath) { 
 
ServletUriComponentsBuilder builder = fromServletMapping(request, basePath); 
 
UriComponents components = UriComponentsBuilder.fromHttpRequest( 
new ServletServerHttpRequest(request)) 
.build(); 
 
String host = components.getHost(); 
if (!hasText(host)) { 
return builder.build(); 
} 
 
builder.host(host); 
builder.port(components.getPort()); 
 
return builder.build(); 
} 
 
private static ServletUriComponentsBuilder fromServletMapping( 
HttpServletRequest request, 
String basePath) { 
 
ServletUriComponentsBuilder builder = fromContextPath(request); 
 
builder.replacePath(prependForwardedPrefix(request, basePath)); 
if (hasText(new UrlPathHelper().getPathWithinServletMapping(request))) { 
builder.path(request.getServletPath()); 
} 
 
return builder; 
} 
 
private static String prependForwardedPrefix( 
HttpServletRequest request, 
String path) { 
 
String prefix = request.getHeader("X-Forwarded-Prefix"); 
if (prefix != null) { 
return prefix + path; 
} else { 
return path; 
} 
} 
} 

  • 7、访问

访问api-gateway微服务的swagger-ui,通过此服务的ui间接访问其他服务的接口文档数据
http://{ip}:{port}/swagger-ui.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359

推荐阅读更多精彩内容