前言
本文仅代表作者的个人观点;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有26篇。
一、API管理对SOAP集成的两种方法
通常情况下,API管理对web API的集成,除了RESTful Web服务之外,还需要管理基于SOAP的Web服务。
SOAP请求包含带有XML有效负载的HTTP POST请求和一些其他特定于SOAP的标头。 这些SOAP请求可以发送到3scale的APIcast网关,转发到后端SOAP服务,并在没有任何自定义的情况下报告给3scale AMP。
但是,如果还需要收集对后端SOAP服务公开的特定操作的请求,进行一定度量和控制(监控、计费等)则需要进行一些额外的设置。
3 Scale对SOAP的两种集成的方法如下:
二、将SOAP服务部署到OpenShift
在本实验中,我将在与3scale AMP相同的,OpenShift集群中的JBoss企业应用程序平台(EAP)容器上,运行现有的基于SOAP的Java EE应用程序。
SOAP的wsdl文件内容如下: / Stores / src / main / resources / wsdl`。
此WSDL遵循SOAP 1.1规范。
SOAP 1.1规范定义了以下命名空间:http://schemas.xmlsoap.org/wsdl/soap/
SOAP 1.2规范定义了以下命名空间:http://schemas.xmlsoap.org/wsdl/soap12/
SOAP规范的两个版本要求使用不同的机制来调用其WSDL中定义的SOAP操作。 在本实验的后期,在使用3scale管理SOAP服务的上下文中,这两个规范的后果将变得明显。
打开源码 Stores / src / main / java / com / redhat / service / StoresWS.java。
检查SOAP Web服务实现:
为Stores API业务服务应用程序创建一个新项目:
创建模板,通过模板部署应用:
部署成功:
在命令行中,查看Stores服务公开的WSDL:执行以下命令以确定新Stores SOAP服务的WSDL的URL:
通过浏览器访问URL:
在新的浏览器选项卡或窗口中,打开URL“http://wsdlbrowser.com”。
提供Stores WSDL的URL,然后单击Browse。
检查WSDL是否已成功导入,以及页面上是否显示了功能列表:
点击getStore---request是xml
查看result,返回值也是xml
截止到目前,SOAP的应用部署成功,并且能被访问。
三、创建APIcast登台和生产路线
创建从OCP router到 API caststaging和production APIcast的、SOAP应用的路由:
在3scale API中配置
创建新服务:
创建application plan
在RHBank账户中创建app:
集成API:
创建method:
创建metris,以便3 scale进行监控:
配置映射关系:
这样,每次向后端Stores服务的任何SOAP操作发出SOAP请求时,POST请求的映射都将增加StoresWS方法的命中。GET请求的映射将增加对诸如Stores服务的WSDL之类的资源的命中。
设置API测试GET请求:
API测试GET请求:/ StoresWS?wsdl
测试API托管SOAP服务
我们能够使用HTTP客户端通过APIcast网关将SOAP请求发送到后端SOAP服务。
通过APIcast登台URL向Stores服务的WSDL发出curl请求来测试API:
curl -k "https://`oc get route stores-soap-policy-staging-route -o template --template {{.spec.host}} -n $OCP_PROJECT_PREFIX-3scale-amp`/StoresWS?wsdl&user_key=$STORES_SOAP_API_KEY"
对Stores Web Service的getAllStores操作发出POST请求: curl -v -k -X POST --header "Content-Type: application/soap+xml" --header "Accept: application/soap+xml" --header "SOAPAction: http://www.rhmart.com/Stores/getAllStores" -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stor="http://www.rhmart.com/Stores/"><soapenv:Header/><soapenv:Body><stor:getAllStores/></soapenv:Body></soapenv:Envelope>' "https://stores-soap-staging-apicast-user11.apps.02bb.openshift.opentlc.com:443/StoresWS?wsdl&user_key=5dac3071cd7ec27c65531882cbfb4fca"
继续使用SOAP 1.1 Web服务,并继续在对该SOAP Web服务的请求中使用名为SOAPAction的HTTP头(上面标黄部分)。
现在,APIcast将使用这些SOAPAction标头字段(: http://www.rhmart.com/Stores/getAllStores)来更新相应的API指标。
选择SOAP,最后结果如下:
单击SOAP策略以展开它。
输入以下映射规则:
四、数据统计 Analytics(分析)
Apicast正确捕获了为每个操作设置的指标。 可以看到endpoint请求的每个SOAPAction对应的命中数。
SOAP 1.2 Web Service的管理类似于您在此处管理SOAP 1.1 Stores Web Service的方法。
不同的地方是:
使用curl实用程序的示例如下:--header application/soap+xml;charset=UTF-8;action="http://www.rhmart.com/Stores/getAllStores".
五、在OpenShift上使用Fuse进行SOAP转换
在上面的实验中,我们解了3scale如何管理SOAP服务。 在APIcast网关中引入了SOAP策略,以便基于SOAP操作收集指标。
集成SOAP的第二种方法,是使用一个中间层,用于公开REST并转换为SOAP。 JBoss Fuse是实现此目的的理想工具。
接下来,我会部署一个camel,用来公开REST端点并路由到之前部署的SOAP服务。
将Fuse应用程序存储到OpenShift。
过一会,pod创建成功:
检查Fuse中间层:
使用您的登录凭据从Web浏览器登录OpenShift管理控制台。
单击Stores-FIS pod,然后单击Open Java Console:
单击Route Diagram。
期望看到定义的所有Camel路线:
或者,单击“源”并查看Camel路径:
公开REST路由以为Stores API提供的不同SOAP操作提供HTTP方法和URL:
注意getStore和getAllStores的两个GET方法,postStore的POST方法和deleteStore操作的DELETE方法。
每个直接路由对应于REST服务中定义的四个操作:
<route customId="true" id="createStore">
<from customId="true" id="_from1" uri="direct:createStore"/>
<setBody customId="true" id="_setBody1">
<simple>${body.getStore()}</simple>
</setBody>
<setHeader customId="true" headerName="soapMethod" id="_setHeader1">
<constant>createStore</constant>
</setHeader>
<to customId="true" id="_to1" uri="direct:soap"/>
</route>
<route customId="true" id="deleteStore">
<from customId="true" id="_from2" uri="direct:deleteStore"/>
<setBody customId="true" id="_setBody2">
<simple resultType="int">${header.storeID}</simple>
</setBody>
<setHeader customId="true" headerName="soapMethod" id="_setHeader2">
<constant>deleteStore</constant>
</setHeader>
<to customId="true" id="_to2" uri="direct:soap"/>
</route>
<route customId="true" id="getStore">
<from customId="true" id="_from3" uri="direct:getStore"/>
<setBody customId="true" id="_setBody3">
<simple resultType="int">${header.storeID}</simple>
</setBody>
<setHeader customId="true" headerName="soapMethod" id="_setHeader3">
<constant>getStore</constant>
</setHeader>
<to customId="true" id="_to3" uri="direct:soap"/>
</route>
<route customId="true" id="getAllStores">
<from customId="true" id="_from4" uri="direct:getAllStores"/>
<setBody customId="true" id="_setBody4">
<mvel>new Object[0]</mvel>
</setBody>
<setHeader customId="true" headerName="soapMethod" id="_setHeader4">
<constant>getAllStores</constant>
</setHeader>
<to customId="true" id="_to4" uri="direct:soap"/>
</route>
上面的每个路由都获取请求,构造CXF请求消息对象,并将标头更新到右侧soapMethod以调用SOAP Web服务。
调用SOAP端点的路由:
<route customId="true" id="soapRoute">
<from customId="true" id="_from5" uri="direct:soap"/>
<toD customId="true" id="tod" uri="cxf:bean:wsStores?defaultOperationName=${header.soapMethod}&exchangePattern=InOut"/>
<setBody customId="true" id="_setBodySoap">
<simple>${body[0]}</simple>
</setBody>
<setHeader customId="true" headerName="Content-Type" id="_setHeaderContextType">
<constant>application/json</constant>
</setHeader>
</route>
在虚拟机中,我们查看FUSE的源代码:
检查项目的application.properties文件中找到的属性:定义了一个名为cxf.endpoint.soap的属性:
jboss@rhtapimgmt ~/lab/3scale_development_labs/StoresFIS (master) $ cat src/main/resources/application.properties
#spring.main.sources=org.mycompany
logging.config=classpath:logback.xml
# the options from org.apache.camel.spring.boot.CamelConfigurationProperties can be configured here
camel.springboot.name=MyCamel
# lets listen on all ports to ensure we can be invoked from the pod IP
server.address=0.0.0.0
management.address=0.0.0.0
# lets use a different management port in case you need to listen to HTTP requests on 8080
management.port=8081
# disable all management endpoints except health
endpoints.enabled = false
endpoints.health.enabled = true
# cxf endpoint address
cxf.endpoint.soap = http://stores-soap:8080
测试Camel REST路由:
将curl请求发送到stores-fis路由以调用REST Web服务,并检查是否已调用SOAP Web服务并将响应转换为application / json:
curl http://`oc get route stores-fis -o template --template {{.spec.host}} -n $OCP_PROJECT_PREFIX-stores-api`/allstores
输出结果是json:
现在可以正确部署REST-SOAP Camel代理,我们可以开始配置APIcast网关以使用此REST端点与SOAP Web服务进行通信。
创建SOAP从router到Stores API staging和production APIcast的路由。:
$ oc create route edge stores-soap-transformation-staging-route \ --service=apicast-staging \ --hostname=stores-staging-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN
$ oc create route edge stores-soap-transformation-production-route \ --service=apicast-production \ --hostname=stores-production-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN
接下来,在3 scale中配置。
创建service:
创建application plan并publish:
在“开发人员”选项卡中,选择RHBank帐户。
单击Applications,然后单击Create Application。
接下来集成JBoss Fuse Camel REST
对路由发起curl请求,输出结果是Json格式。
jboss@rhtapimgmt / $ curl -k https://stores-staging-apicast-user11.apps.02bb.openshift.opentlc.com:443/allstores?user_key=1f5ad4d8206ff559fe1a6ae0f715b9a4
{"store":[{"storeID":1,"storeName":"Downtown\n Store","storeLat":-34.6052704,"storeLong":-58.3791766},{"storeID":2,"storeName":"EastSide\n Store","storeLat":-34.5975668,"storeLong":-58.3710199},{"storeID":3,"storeName":"?","storeLat":0.0,"storeLong":0.0},{"storeID":4,"storeName":"?","storeLat":0.0,"storeLong":0.0}]}jboss@rhtapimgmt / $
六、OData Service Management
在本节中,我们将部署OData(开放数据协议)服务。 OData是一种标准,它定义了一组用于构建和使用RESTful API的最佳实践。 我们部署的服务基于JBoss Data Virtualization for OpenShift Container Platform虚拟数据库(VDB)。 此VDB具有一个虚拟视图,可从两个数据库表(MySQL和PostgreSQL)中检索数据,并将它们显示为单个SQL ANSI表。 然后,开箱即用,该视图在JBoss Data Virtualization中作为OData REST服务公开。
将Stock API项目部署到OpenShift
在本节中,我们将Stock API部署到在OpenShift上运行的JBoss EAP容器中。 库存数据存在于两个数据库中:MySQL和PostgreSQL。 JBoss Data Virtualization用于提供数据虚拟化,并将组合数据视图显示为OData REST服务。
部署mysql和PostgreSQL(篇幅有限,步骤省略)
对应用发起curl,可以得到正常返回结果。
curl -k http://stock-api-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN/odata4/Stock-API/FederatedStock/stock?$format=JSON
REST odata服务现在已正确部署,现在可以开始配置APIcast网关以使用此REST端点与服务进行通信。
创建APIcast Staging和Production Routes
oc create route edge stock-staging-route \
--service=apicast-staging \
--hostname=stock-staging-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN
oc create route edge stock-production-route \
--service=apicast-production \
--hostname=stock-production-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN
登录3scale,进行配置。
创建app plan并publish:
在“开发人员”选项卡中,单击“RHBank”。
单击Applications,然后单击Create Application:
创建应用:
七、API开发人员注册流程制定:单个应用注册
接下来,我们创建自定义注册流程,以管理通过3scale管理的API的开发人员注册。 涵盖的一些方案是单个应用程序,多个应用程序注册,组成员流程。
达成的目标是:
在3scale内容管理系统(CMS)中找到的开箱即用的主页包含一个嵌入其中的非常简单的注册流程。
在本实验的这一部分中,我们将提取该嵌入式注册流并将其另存为“Partial”。 然后,在您的主页中引用该新Partial。实验中,我们只是将原始注册流逻辑从隐藏在主页中转移到被引用的部分,所以不会改变Developer Portal的外观。
在本实验的后续部分中,我们实现更复杂的注册流程,这些注册流程也实现为部分流程。 只需将引用更改为所需的Partial,即可轻松地将所有注册流程交换到Developer Portal主页中。
在3scale管理员门户中,单击顶部工具栏中的Developer Portal链接以导航到CMS。观察Developer Portal主页的默认外观。在CMS的顶部,单击:访问Developer Portal
导航至:My→Root→homepage:
拷贝代码119 - 182行。
然后新建Partial
修改homepage:
发布两个变更
通过这种方式创建的主业,默认只有两个application plan:
我们要做的是,让主业可以看到所有的application plan:
代码如下:
查看文件:
jboss@rhtapimgmt ~/lab/3scale_development_labs/DevPortal (master) $ cat _single_app_signup_form.html.liquid
<div class="container">
<h1>Pick your plan</h1>
<br/>
{% for service in provider.services %}
<h2> {{ service.name }} </h2>
<div class="row">
{% for plan in service.application_plans%}
<div class="col-md-4">
<article class="panel panel-default">
<div class="panel-heading">
<strong>{{ plan.name }}</strong>
</div>
<div class="panel-body">
<div class="row">
{% if plan.features == present %}
<div class="col-md-6">
<h5>Features</h5>
<ul class="features list-unstyled">
{% for feature in plan.features %}
<li>
<i class="fa fa-check"></i> {{ feature.name }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="col-md-6">
<h5>Limits</h5>
<ul class="limits list-unstyled">
{% if plan.usage_limits == present %} {% for limit in plan.usage_limits %}
<li>
<i class="fa fa-signal"></i> {{ limit.metric.name }} – {{ limit.value }} {{ limit.metric.unit }}s per {{ limit.period }}
</li>
{% endfor %} {% else %}
<li>
<i class="fa fa-signal"></i> No limits
</li>
{% endif %}
</ul>
</div>
</div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-12">
<a class="btn btn-cta-secondary pull-right" href="{{ urls.signup }}?{{ plan | to_param }}&{{ service.service_plans.first | to_param }}">Signup to plan {{ plan.name }}</a>
</div>
</div>
</div>
</article>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
将此partial添加到开发人员门户。
编辑主页:
在第120行附近,将original_app_signup_form替换为single_app_signup_form。
现在点击访问开发者门户,此时可以看到所有的application plan:
八、API开发人员注册流程制定:单个应用注册
多应用程序注册流程允许用户同时注册多个服务(以及相关的application paln)。它通过提供在Developer Portal中呈现多选复选框HTML表单的部分来实现。
确保“多个应用程序”功能被启用
参照源码:
$ cat _multiple_app_signup_form.html.liquid
<form action="{{ urls.signup }}" method="get">
<div class="container">
<h1>Pick your plan</h1>
<br/>
{% for service in provider.services %}
<h2> {{ service.name }} </h2>
<div class="row">
{% for plan in service.application_plans%}
<div class="col-md-4">
<article class="panel panel-default">
<div class="panel-heading">
<strong>{{ plan.name }}</strong>
</div>
<div class="panel-body">
<div class="row">
{% if plan.features == present %}
<div class="col-md-6">
<h5>Features</h5>
<ul class="features list-unstyled">
{% for feature in plan.features %}
<li>
<i class="fa fa-check"></i> {{ feature.name }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="col-md-6">
<h5>Limits</h5>
<ul class="limits list-unstyled">
{% if plan.usage_limits == present %} {% for limit in plan.usage_limits %}
<li>
<i class="fa fa-signal"></i> {{ limit.metric.name }} – {{ limit.value }} {{ limit.metric.unit }}s per {{ limit.period }}
</li>
{% endfor %} {% else %}
<li>
<i class="fa fa-signal"></i> No limits
</li>
{% endif %}
</ul>
</div>
</div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="col-md-12">
<input type="checkbox" name="plan_ids[]" value="{{ plan.id }}">Signup to {{ plan.name }}</input>
<input type="hidden" name="plan_ids[]" value="{{ service.service_plans.first.id }}"></input>
</div>
</div>
</div>
</div>
</div>
</article>
</div>
{% endfor %}
</div>
{% endfor %}
<div class="container text-center">
<button type="submit" class="btn btn-cta-primary">Signup</a>
</div>
</div>
</form>
创建:multi_app_signup_form partial。
修改homepage:
将
修改成:
发布变更后,登录开发者界面,可以看到application plan的复选框:
我们选择两个app plan,API Unlimited 和ProductsBasicPlan,注册账户:
然后到admin门户激活:
然后用新建的账户登录开发者界面,可以看到两个应用。
魏新宇