前言
本文仅代表作者的个人观点;
本篇书写过程中,咨询了我的同事舒力,Kylin,在此表示感谢;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有26篇。
一、Swagger
3 Scale有个很好的功能,它提供ActiveDocs实时文档。它基于Swagger框架,提供了一种记录API的方法,并包含在Developer Portal中。
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、先后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架。
那么,swagger档如何生成?
在源码中进行如下定义,应用运行的时候,会自动生成。
二、一个Restful API例子的源码分析
我们看一个Restful API,这个API是可以查询种子信息的。
的两个源码文件:
我们看一下这个Restful API的源码:
查看第一个源码文件,它定义了root 上下文:
$ cat ProductsApplication.java
package com.redhat.service;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import io.swagger.jaxrs.config.BeanConfig;
@ApplicationPath("rest")
public class ProductsApplication extends Application {
public ProductsApplication(){
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/rest");
beanConfig.setResourcePackage("com.redhat.service");
beanConfig.setScan(true);
beanConfig.setTitle("Products");
beanConfig.setDescription("RHMart's Products API");
beanConfig.setPrettyPrint(true);
}
}
查看第二个源码文件,ProductServices.java它定义了http方法和path:
$ cat ProductServices.java
package com.redhat.service;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.redhat.model.JsonResponse;
import com.redhat.model.Product;
import com.redhat.model.ProductDao;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Path("/services")
@Api(value="services")
@Produces("application/json")
public class ProductServices {
@Inject
ProductDao productDAO;
@ApiOperation(value="Get all Products")
@Path("/products")
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Product> getAllProducts(){
List<Product> prod= productDAO.getAll();
return prod;
}
@ApiOperation(value="Get a Product by ID")
@Path("/product/{productId}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Product getProduct(@PathParam("productId") Integer productId){
return productDAO.getProductById(productId);
}
@ApiOperation(value="Create a new Product")
@Path("/product")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public JsonResponse createProduct(Product product){
JsonResponse jr = new JsonResponse("");
try{
productDAO.createProduct(product);
}catch(Exception e){jr.setMessage(e.getMessage()); return jr;};
jr.setMessage("Product created");
return jr;
}
@ApiOperation(value="Delete a Product by ID")
@Path("/product/{productId}")
@DELETE
@Produces(MediaType.APPLICATION_JSON)
public JsonResponse deleteProduct(@PathParam("productId") Integer productId){
JsonResponse jr = new JsonResponse("");
try{
productDAO.deleteProduct(productId);
}catch (Exception e) { jr.setMessage(e.getMessage()); return jr;};
jr.setMessage("Product " productId " deleted");
return jr;
}
}
我们再看看之前curl的链接:curl -v -k http://products-api-david-products-api.apps.na39.openshift.opentlc.com/rest/services/product/1
上面黄色部分的rest,就是root上下文,在 ProductsApplication.java中定义的;
红色部分的/service,是定义的path,在ProductServices.java中定义的;
绿色部分的/product/{productId},是定义的path,在ProductServices.java中定义的;
那么,源码如何与数据库进行通讯呢?
查看源码中的数据访问对象: List<Product> prod= productDAO.getAll(),找到import的内容:
import com.redhat.model.ProductDao;
切换到import com.redhat.model目录中,查看源码文件:
先查看ProductDao.java,它定义了接口:
在查看访问数据库的实现,可以看到,通过CDJ注入了EM,也就是一个实体类,unitName="primary"
接下来,我们再查看unitName="primary"的出处,查看./src/main/resources/META-INF/persistence.xml文件,里面定义了primary的datasource,通过JPA的方式访问数据库:
/Products/src/main/webapp/WEB-INF/Products-ds.xml中进行查看:
上图可以看出,应用对jdbc的访问,最终是:
jdbc:postgresql://productsdb:5432/RHMart
而productsdb:5432/,实际上就是数据库的pod SVC。也就是说,应用的pod,通过SVC方式访问数据库。这方访问方式,叫application datasource。
还有一种叫server datasource。这种情况下,将JDBC的配置在app server的配置文件中,如EAP的standalone.xml中。
三、Restful API在OCP上的部署
在OCP上,为Products API应用程序创建一个新项目:
部署和测试产品API服务
将products-api模板导入OpenShift环境:
使用products-api模板创建一个新应用程序:
测试product API服务是否接受请求并返回正确的响应:
返回结果:
可以通过执行以下命令检索所有产品的列表:
[
{
"productid": 1,
"productname": "Ninja Blender",
"productprice": 320.0
},
{
"productid": 2,
"productname": "Ninja Blender Pro",
"productprice": 515.0
},
{
"productid": 3,
"productname": "Kicthenhelp Juicer",
"productprice": 149.99
},
{
"productid": 4,
"productname": "ArtCuisine Toaster",
"productprice": 79.99
},
{
"productid": 5,
"productname": "White and Decor Toaster Oven",
"productprice": 49.99
},
{
"productid": 6,
"productname": "Mexpresso Maker",
"productprice": 199.99
},
{
"productid": 7,
"productname": "Mini Fridge",
"productprice": 229.99
},
{
"productid": 8,
"productname": "Slow-Cooker Pot",
"productprice": 44.99
},
{
"productid": 9,
"productname": "SungSamn 4-Door Refrigerator",
"productprice": 2199.99
},
{
"productid": 10,
"productname": "Hanilton 12 cup Food Processor",
"productprice": 49.99
}
]
于PostgreSQL的Products数据库中的种子列表。
四、通过swagger工具分析swagger文件
product service提供了一个swagger.yaml配置文件,用于记录它公开的资源。
要访刚才应用的swagger.yaml配置文件,请将浏览器指向以下命令的输出:
echo http://"$(oc get route/products-api -o template --template {{.spec.host}})"/rest/swagger.yaml
结果:
http://products-api-david-products-api.apps.na39.openshift.opentlc.com/rest/swagger.yaml
我们看一下swagger文件中的内容:
---
swagger: "2.0"
info:
description: "RHMart's Products API"
version: "1.0.2"
title: "Products"
host: "localhost:8080"
basePath: "/rest"
tags:
- name: "services"
schemes:
- "http"
paths:
/services/product:
post:
tags:
- "services"
summary: "Create a new Product"
description: ""
operationId: "createProduct"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "body"
required: false
schema:
$ref: "#/definitions/Product"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/JsonResponse"
/services/product/{productId}:
get:
tags:
- "services"
summary: "Get a Product by ID"
description: ""
operationId: "getProduct"
produces:
- "application/json"
parameters:
- name: "productId"
in: "path"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Product"
delete:
tags:
- "services"
summary: "Delete a Product by ID"
description: ""
operationId: "deleteProduct"
produces:
- "application/json"
parameters:
- name: "productId"
in: "path"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/JsonResponse"
/services/products:
get:
tags:
- "services"
summary: "Get all Products"
description: ""
operationId: "getAllProducts"
produces:
- "application/json"
parameters: []
responses:
200:
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/Product"
definitions:
Product:
type: "object"
properties:
productid:
type: "integer"
format: "int32"
productname:
type: "string"
productprice:
type: "number"
format: "double"
JsonResponse:
type: "object"
properties:
message:
type: "string"
我们使用在线https://editor.swagger.io/编辑器,导入刚下载的yaml文件。
导入以后,就可以看到四个http定义的方法:
将代码的地址从localhost:8080改成实际地址(第六行):
我们展开GET,点击try it out:
ID号输入1
查看执行方法:
查看执行结果:
接下来,我们换一个path测试,使用:
点击try it out:
输出结果是所有信息:
魏新宇
- 红帽资深解决方案架构师
- 专注开源云计算、容器及自动化运维在金融行业的推广
- 拥有MBA、ITIL V3、Cobit5、C-STAR、TOGAF9.1(鉴定级)等管理认证。
- 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。