在分享培训了swagger对于API的设计之后,有一些人问我说:你看,现在咱们前端使用web_API做为mock data在进行测试,后端也有mock 测试。然后我们再进行联调,这之中肯定会出现一些偏差。有没有一种方案是:前端不用写mock数据测试,从一开始,就由后端提供一个在线运行的服务,以此减少后期联调的工作量!   答案是:可以!


当然,在最开始,由于我近期在做API网关的一些工作,我直接简单粗暴的,将swagger的API文档,导入了网关,由网关做了一个mock服务提供。额,现在想来很不负责哈(因为我在做那一块的内容,图省事儿,并没有过多去想当时那个项目的现状),后来想了想,那个项目并不需要专门搭建网关去做这么个服务,并且由于它的架构设计,如果再增加网关服务,会额外增添一些工作量和难度。然后,我看了看笔记,再溜了一圈swagger的网站,得出的解决方案为:

1,利用codegen立即生成可运行的工程

2,直接将服务注册到Dubbo,以供前端使用


备注:公司对于Dubbo这块内容很熟悉了,我主要介绍一下从swagger的API文档,生成可部署工程的过程(其实,这中间还有很多需要进行个人总结,比如嵌入式服务器Jetty,还有我解决一些问题的过程思考和思路总结)!

一、使用swagger Codegen生成可部署工程

这个特别简单,真的特别简单,简单得不能再简单。(当然,我用百度,或者说查到的中文资料,关于从API生成到工程,几乎为零。不过,好在后期集成swagger的还不少,虽然比较复杂)

首先,是看swagger官方对于codegen是怎么说的:https://swagger.io/docs/swagger-tools/#installation-11 

然后,再看看具体的命令怎么写:https://github.com/swagger-api/swagger-codegen/wiki/Server-stub-generator-HOWTO

最后,看看对于swagger editor示例中的API,在各种语言下生成的工程示例:https://github.com/swagger-api/swagger-codegen  备注:具体的语言示例路径为:https://github.com/swagger-api/swagger-codegen/tree/master/samples/server/petstore


实例:比如说我用swagger编辑了一个basic API,那么生成JAX-RS(Jersey)的工程命令如下:

java -jar J:\swagger-codegen-cli-2.2.1.jar generate -i "C:\Users\10283_000\Desktop\API lifecycle\Basic.json" -l jaxrs -o jaxrs/jersey2


简单粗暴,然后,运行方式为,在工程的Pom.xml同路径下,执行:mvn jetty:run

然后,没你啥事儿了,开始用吧!  这是一个运行状态的服务,你有两种方式,可以把这个服务变成一个mock service,第一:也就是比较省事儿的,直接在编辑API文档的时候,给设置一个example值; 第二:在这个工程中,设置其指定返回值


这个过程,对于我个人来说,要反思的内容,有点多,额!!!我感觉,很有必要给贴一下代码,再次强调,我只执行了一行命令,别的,我什么都没有做:看看主要的几个点吧(以JAX-RS:Jersey为例):


这是整个项目工程的结构图,所有的代码均是自动生成。开发人员,只需要专注于我用红圈画起来的类的实现就OK了!  其他的一些公共问题,比如说跨域,JSON,异常等,已经被简单处理过。如果要求不是特别高,特别严密,那么直接使用是完全可以的! 再,展示一下关键的类(额,用的swagger API 是我第一次学的时候导出的JSON文件,并不是很完善,吐槽不要太过分)

API接口类:

package io.swagger.api;

import io.swagger.model.*;
import io.swagger.api.ResourcesApiService;
import io.swagger.api.factories.ResourcesApiServiceFactory;

import io.swagger.annotations.ApiParam;
import io.swagger.jaxrs.*;

import io.swagger.model.Build;

import java.util.List;
import io.swagger.api.NotFoundException;

import java.io.InputStream;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;

import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.*;

@Path("/resources")
@Consumes({ "application/json" })
@Produces({ "application/json" })
@io.swagger.annotations.Api(description = "the resources API")
@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2017-08-21T09:59:53.975+08:00")
public class ResourcesApi  {
   private final ResourcesApiService delegate = ResourcesApiServiceFactory.getResourcesApi();

    @POST
    @Path("/api/tb-build")
    @io.swagger.annotations.ApiOperation(value = "", notes = "新增一个建筑", response = void.class, tags={ "建筑", })
    @io.swagger.annotations.ApiResponses(value = { 
        @io.swagger.annotations.ApiResponse(code = 200, message = "新增建筑成功!", response = void.class),
        
        @io.swagger.annotations.ApiResponse(code = 404, message = "找不到API服务", response = void.class),
        
        @io.swagger.annotations.ApiResponse(code = 200, message = "未知错误", response = void.class) })
    public Response addBuild(@ApiParam(value = "Pet object that needs to be added to the store" ,required=true) Build body
,@Context SecurityContext securityContext)
    throws NotFoundException {
        return delegate.addBuild(body,securityContext);
    }
    @GET
    @Path("/api/tb-build")
    @io.swagger.annotations.ApiOperation(value = "", notes = "获取所有的建筑信息", response = void.class, tags={ "建筑", })
    @io.swagger.annotations.ApiResponses(value = { 
        @io.swagger.annotations.ApiResponse(code = 200, message = "操作成功", response = void.class),
        
        @io.swagger.annotations.ApiResponse(code = 404, message = "找不到API服务", response = void.class),
        
        @io.swagger.annotations.ApiResponse(code = 200, message = "未知错误", response = void.class) })
    public Response getBuild(@ApiParam(value = "Status values that need to be considered for filter",required=true) @QueryParam("buildId") String buildId
,@Context SecurityContext securityContext)
    throws NotFoundException {
        return delegate.getBuild(buildId,securityContext);
    }
}
APIService类:

package io.swagger.api;

import io.swagger.api.*;
import io.swagger.model.*;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;

import io.swagger.model.Build;

import java.util.List;
import io.swagger.api.NotFoundException;

import java.io.InputStream;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2017-08-21T09:59:53.975+08:00")
public abstract class ResourcesApiService {
    public abstract Response addBuild(Build body,SecurityContext securityContext) throws NotFoundException;
    public abstract Response getBuild(String buildId,SecurityContext securityContext) throws NotFoundException;
}
APIServiceImpl类:

package io.swagger.api.impl;

import io.swagger.api.*;
import io.swagger.model.*;

import io.swagger.model.Build;

import java.util.List;
import io.swagger.api.NotFoundException;

import java.io.InputStream;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2017-08-21T09:59:53.975+08:00")
public class ResourcesApiServiceImpl extends ResourcesApiService {
    @Override
    public Response addBuild(Build body, SecurityContext securityContext) throws NotFoundException {
        // do some magic!
        return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
    }
    @Override
    public Response getBuild(String buildId, SecurityContext securityContext) throws NotFoundException {
        // do some magic!
        return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
    }
}

额,其他具体的,还得自己去看看,也不用自己写,就拿提供的示例看看就行! 有些还是有点区别,比如说JAX-RS(Jersey)这里面用到的是抽象类,而springmvc用的是接口

二、从项目工程集成Swagger生成在线API文档

其实,我很不想说这一点的,因为写这个集成的有很多,我也看了很多,我之所以写它,是因为我感觉我找到的这种方式,是相对比较简单的。代码写完后,集成swagger做基本的文档处理,其实特别简单,简单到只有两步:

第一:在pom文件中,添加依赖(版本的话,你随意)

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.5.0</version>
        </dependency>
 
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.5.0</version>
        </dependency>

第二:在springmvc的配置文件中,添加对于swagger文档静态资源的访问授权

    <bean class="springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration" id="swagger2Config"/>  
    <mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/>  
    <mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/> 

有了这两步之后,就可以得到一个这样的文档

第三步:通过使用swagger的API注解,增添一些额外的注释说明

三、总结

其实,这些东西,都挺容易的。但中间也遇到了一些问题,也包括因为自己主要所在项目是java EE体系的,自己就主要关心、解决java EE这边可能会面临的问题,但对于公司其他项目,相对来说,并不那么上心。这个态度,真是很low啊!

接下来,会总结一些在这个过程之中的一些思维方式,以及遇到的一些问题和解决过程、解决方案!


posted on 2017-08-21 17:56  何红霞  阅读(368)  评论(0编辑  收藏  举报