一、 Swagger介绍
Swagger™的目标是为REST APIs 定义一个标准的,与语言无关的接口,使人和计算机在看不到源码或者看不到文档或者不能通过网络流量检测的情况下能发现和理解各种服务的功能。当服务通过Swagger定义,消费者就能与远程的服务互动通过少量的实现逻辑。类似于低级编程接口,Swagger去掉了调用服务时的很多猜测。
二、 如何集成swagger到我们的项目中
1. 首先添加maven依赖:
<!-- swagger-springmvc -->
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-models</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.3.11</version>
</dependency>
<!-- swagger-springmvc dependencies -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>15.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.1.0</version>
</dependency>
2. 后台配置(配置API的描述)
途径一:
在spring的applicationcontext.xml文件中,添加配置(或者使用注解方式@EnableSwagger)
<mvc:annotation-driven/>
<bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />
<!-- 使用注解的包,包括子集 -->
<context:component-scan base-package="com.java1234.controller" />
<!-- 自动扫描 -->
<context:component-scan base-package="com.java1234.service" />
解释:swagger会扫描 base-package包下的所有类,生成api文档(本项目中是指配置扫描包下的所有类)
途径二:
如果想进一步的定制,比如只扫描某个包下的某些路径,那么就需要用到定制类,途径二是基于途径一在功能上的扩展,所以要想达到定制效果首先将途径一配置好。(无非就是
或者在SwaggerConfig的类前面加上@EnableSwagger)
新建MySwaggerConfig类,编写SwaggerConfig配置API的解释形式:配置基本都差不多,就是在形成API文档时对文档的描述
如下图:
package com.java1234.controller;
import com.mangofactory.swagger.configuration.SpringSwaggerConfig;
import com.mangofactory.swagger.models.dto.ApiInfo;
import com.mangofactory.swagger.plugin.EnableSwagger;
import com.mangofactory.swagger.plugin.SwaggerSpringMvcPlugin;
import org.springframework.beans.factory.annotation.Autowired;
/*import org.springframework.context.annotation.Bean;*/
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 项目名称:apidoc
*
* @description:
* @author Wind-spg
* @create_time:2015年2月10日 上午10:27:51
* @version V1.0.0
*
*/
@Configuration
@EnableSwagger
@ComponentScan(basePackages = {"com.java1234.controller"})
// Loads the spring beans required by the framework
public class MySwaggerConfig
{
private SpringSwaggerConfig springSwaggerConfig;
/**
* Required to autowire SpringSwaggerConfig
*/
@Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig)
{
this.springSwaggerConfig = springSwaggerConfig;
}
/**
* Every SwaggerSpringMvcPlugin bean is picked up by the swagger-mvc
* framework - allowing for multiple swagger groups i.e. same code base
* multiple swagger resource listings.
*/
@Bean
public SwaggerSpringMvcPlugin customImplementation()
{
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig).apiInfo(apiInfo()).includePatterns(
".*?");
}
private ApiInfo apiInfo()
{
ApiInfo apiInfo = new ApiInfo(
"xxx system API ",
"controller API",
"My Apps API terms of service",
"xxx",
null,
null);
return apiInfo;
}
}
(Ps:在该文件头注解@ComponentScan(basePackages = {“com.java1234.controller”})扫描controller包位置,包含子包。customImplementation方法的includePatterns可以进行过滤)
3. Controller样例:
package com.java1234.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.java1234.entity.Blog;
import com.java1234.lucene.BlogIndex;
import com.java1234.service.BlogService;
import com.java1234.service.CommentService;
import com.java1234.util.StringUtil;
/**
* 博客Controller层
* @author Administrator
*
*/
@Controller
@RequestMapping("/blog")
@Api(value = "blog",description = "博客管理")
public class BlogController {
@Resource
private BlogService blogService;
@Resource
private CommentService commentService;
// 博客索引
private BlogIndex blogIndex=new BlogIndex();
/**
* 请求博客详细信息
* @return
* @throws Exception
*/
@RequestMapping("/articles/{id}")
@ApiOperation(value = "根据博客ID查询博客详细信息", httpMethod = "GET", response = Blog.class, notes = "根据博客ID查询博客详细信息")
public ModelAndView details(@ApiParam(required = true, name = "id", value = "用户信息json数据") @PathVariable("id") Integer id, HttpServletRequest request)throws Exception{
ModelAndView mav=new ModelAndView();
Blog blog=blogService.findById(id);
String keyWords=blog.getKeyWord();
if(StringUtil.isNotEmpty(keyWords)){
String arr[]=keyWords.split(" ");
mav.addObject("keyWords",StringUtil.filterWhite(Arrays.asList(arr)));
}else{
mav.addObject("keyWords",null);
}
mav.addObject("blog", blog);
blog.setClickHit(blog.getClickHit()+1); // 博客点击次数加1
blogService.update(blog);
Map<String,Object> map=new HashMap<String,Object>();
map.put("blogId", blog.getId());
map.put("state", 1); // 查询审核通过的评论
mav.addObject("commentList", commentService.list(map));
mav.addObject("pageCode", this.genUpAndDownPageCode(blogService.getLastBlog(id),blogService.getNextBlog(id),request.getServletContext().getContextPath()));
mav.addObject("mainPage", "foreground/blog/view.jsp");
mav.addObject("pageTitle",blog.getTitle()+"_Java开源博客系统");
mav.setViewName("mainTemp");
return mav;
}
/**
* 根据关键字查询相关博客信息
* @param q
* @return
* @throws Exception
*/
@RequestMapping("/q")
public ModelAndView search(@RequestParam(value="q",required=false)String q,@RequestParam(value="page",required=false)String page,HttpServletRequest request)throws Exception{
if(StringUtil.isEmpty(page)){
page="1";
}
ModelAndView mav=new ModelAndView();
mav.addObject("mainPage", "foreground/blog/result.jsp");
List<Blog> blogList=blogIndex.searchBlog(q.trim());
Integer toIndex=blogList.size()>=Integer.parseInt(page)*10?Integer.parseInt(page)*10:blogList.size();
mav.addObject("blogList",blogList.subList((Integer.parseInt(page)-1)*10, toIndex));
mav.addObject("pageCode",this.genUpAndDownPageCode(Integer.parseInt(page), blogList.size(), q,10,request.getServletContext().getContextPath()));
mav.addObject("q",q);
mav.addObject("resultTotal",blogList.size());
mav.addObject("pageTitle","搜索关键字'"+q+"'结果页面_Java开源博客系统");
mav.setViewName("mainTemp");
return mav;
}
/**
* 获取下一篇博客和下一篇博客代码
* @param lastBlog
* @param nextBlog
* @return
*/
private String genUpAndDownPageCode(Blog lastBlog,Blog nextBlog,String projectContext){
StringBuffer pageCode=new StringBuffer();
if(lastBlog==null || lastBlog.getId()==null){
pageCode.append("<p>上一篇:没有了</p>");
}else{
pageCode.append("<p>上一篇:<a href='"+projectContext+"/blog/articles/"+lastBlog.getId()+".html'>"+lastBlog.getTitle()+"</a></p>");
}
if(nextBlog==null || nextBlog.getId()==null){
pageCode.append("<p>下一篇:没有了</p>");
}else{
pageCode.append("<p>下一篇:<a href='"+projectContext+"/blog/articles/"+nextBlog.getId()+".html'>"+nextBlog.getTitle()+"</a></p>");
}
return pageCode.toString();
}
/**
* 获取上一页,下一页代码 查询博客用到
* @param page 当前页
* @param totalNum 总记录数
* @param q 查询关键字
* @param pageSize 每页大小
* @param projectContext
* @return
*/
private String genUpAndDownPageCode(Integer page,Integer totalNum,String q,Integer pageSize,String projectContext){
long totalPage=totalNum%pageSize==0?totalNum/pageSize:totalNum/pageSize+1;
StringBuffer pageCode=new StringBuffer();
if(totalPage==0){
return "";
}else{
pageCode.append("<nav>");
pageCode.append("<ul class='pager' >");
if(page>1){
pageCode.append("<li><a href='"+projectContext+"/blog/q.html?page="+(page-1)+"&q="+q+"'>上一页</a></li>");
}else{
pageCode.append("<li class='disabled'><a href='#'>上一页</a></li>");
}
if(page<totalPage){
pageCode.append("<li><a href='"+projectContext+"/blog/q.html?page="+(page+1)+"&q="+q+"'>下一页</a></li>");
}else{
pageCode.append("<li class='disabled'><a href='#'>下一页</a></li>");
}
pageCode.append("</ul>");
pageCode.append("</nav>");
}
return pageCode.toString();
}
}
@api,用在类上,用于解释整个类。
@apioperation,用于方法上,value是笼统的介绍方法作用,notes是详细的说明方法作用
4. 去 https://github.com/swagger-api/swagger-ui 下载zip包,解压后,将dist文件夹下的所有内容copy到,Java web project的webapp下,比如,
在spring-mvc.xml配置文件里配置扫描路径
<mvc:resources mapping="/static/**" location="/static/"/>
修改index.html文件
url = “http://localhost:8080/Blog/api-docs“;
其中Blog为你的项目名
5. 运行项目,在浏览器访问http://localhost:8080/Blog/stctic/swagger/index.html即可看到效果
三、 碰到的问题
(一) 关于路径问题:
可以删除其中Blog,只保留http://localhost:8080/api-docs
其次可以检查一下web.xml文件servlet-mapping
另外,GitHub上的swagger-ui的demo有些问题,可以找网上别人项目用的demo来替换;
(二) 关于@API注解
在Swagger Annotation中: API表示一个开放的API,可以通过description简要描述该API的功能。
在一个@API下,可有多个@ApiOperation,表示针对该API的CRUD操作。
在ApiOperation Annotation中可以通过value,notes描述该操作的作用,response描述正常情况下该请求的返回对象类型。
在一个ApiOperation下,可以通过ApiResponses描述该API操作可能出现的异常情况。
ApiParam用于描述该API操作接受的参数类型再接着,为项目的Model对象添加Swagger Annotation,这样Swagger Scanner可以获取更多关于Model对象的信息。