即将推出的kbmMW更新不仅是一些bug修正,同时将包含一个新的主要功能:客户端存根生成器框架。

那什么是客户端存根生成器框架呢?

他是一个基于kbmMW smart services,可以生成由各种类型的客户端直接使用的代码,以访问基于kbmMW应用服务器的HTTP smart services。(什么是HTTP Smart Service呢?可参考洞主写的文章:kbmmw 的HTTP Smart Service入门)

当前,kbmMW已经实现智能客户端(smart client)功能,通过这个功能,非常容易实现客户端来访问smart services中的功能。由于智能客户端依赖于后期绑定,所以开发者无法通过IDE及编译器的帮助,来获得有关参数及其类型。因此,编译器还需要生成一些代码,为IDE及编译器,来解释服务端发布的函数及方法。

上面这个问题,正是客户端存根生成器要解决的问题,但是,本文不会讨论如何为智能服务生成Delphi客户端存根,因为尽管已经做了这方面的准备工作,但这个特定功能可能不会在即将发布的版本中完整实现。相反,我将介绍利用存根生成器框架来实现另一个更复杂的代码集,满足REST世界中的典型需求。一年多前,当我咨询大公司做Java代码时,认识到了这一点。

在Java世界中,使用当时被称为Swagger的东西来记录REST接口,这是一个事实上的标准。后来它被重命名为OpenAPI,现在得到了绝大多数支持REST的开发者的认可和支持。

OpenAPI提供了REST接口的描述,可以用于文档,也可以用于为各种开发环境自动生成(存根)代码,使这些环境可以轻松利用通过REST接口发布的功能。(Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。)

您可以在此处阅读有关OpenAPI的更多信息:

https://swagger.io/

https://blog.csdn.net/sanyaoxu_2/article/details/80555328

OpenAPI不仅成为事实上的标准,还生成了可用于生成,编辑,查看和测试REST接口描述的各种工具(通常称为Swagger文件)。

其中一个工具是Swagger-UI(用户界面),它由Javascript和HTML组成,可以由Web服务器提供,为服务器中公开的REST接口提供简单易用的用户界面。

kbmMW现在完全支持所有这些。

让我们简单地开始使用Swagger-UI展示kbmMW SimpleInvocation演示服务器的REST接口是什么样的:

在左侧,可以看到公开的REST接口的OpenAPI声明,右侧可以看到用户友好的界面,通过简单的按钮点击可以调用这些界面。填充参数甚至REST方法也很容易。

如果我向右滚动到AddNumbers方法,然后单击该栏,它会打开其他信息,以及一个让我们尝试REST调用的按钮。

这非常酷!

那么我们如何使支持REST的应用服务器启用OpenAPI呢?

这真的很容易。

要返回REST服务的OpenAPI规范,我们只需在Unit2中向服务添加另一个REST公开方法。

     [kbmMW_Rest('method:get, path: "api", responseMimeType:"application/x-yaml"')]
function OpenAPI:string;

这可以根据您的喜好来命名方法及其REST路径,但我们应该提供正确的responseMimeType。OpenAPI描述的标准用YAML表示,幸运的是kbmMW完全支持。它也允许在JSON中生成OpenAPI描述,但它更像是一种与不支持YAML的系统兼容的方法。所以在这个例子中,我们在responseMimeType中指定响应类型为YAML。

// Return OpenAPI specification.
function TkbmMWCustomService2.OpenAPI:string;
begin
// Return OpenAPI specification for all REST methods in this service
// as YAML. Add the ASettings value: 'json:true' to return the specification
// as JSON.
// Add 'servers: [ "url1", "url2",.. "urln" ]' to ASettings if you want to
// embed server location information in the specification.
// Add 'inline:true' to inline object definitions instead of using $ref.
// The example in the next line utilize the configuration framework to make
// the setting easily configurable.
Result:=TkbmMWSmartOpenAPIStubGenerator.GenerateOpenAPI('',self,'inline:$(OpenAPI.inline=false)');
end;

OpenAPI函数的实现代码非常简单,只调用OpenAPI存根生成器的GenerateOpenAPI方法,服务为“OpenAPI'ified”,并可选择设置字符串。设置字符串可以为空,在这个示例中,设置字符串包含值为:

inlinei:$(OpenAPI.inline=false)

原因是有两种有效的方法可以为REST接口生成OpenAPI规范,这些接口可以内联或通过引用获取或返回对象。

内联意味着每个对象都会详细解释它可以在REST调用的所有描述中使用的每个位置,而引用意味着在需要时描述和引用OpenAPI样式组件(对象)。引用是默认值。但是在示例代码中,我选择在kbmMW配置框架的帮助下对其进行配置。我们可以编写:inline:false或inline:true,但是样本会询问配置OpenAPI.inline值的当前值,该值可以是true或false。如果没有找到这样的值,kbmMW将使用默认值false(如=后面所示)。

也可以指定JSON是首选。这只需要将json:true添加到设置字符串,如下所示:

inline:$(OpenAPI.inline=false), json:true

显然,这也可以像内联一样进行配置。

但是我们保持原样,因此OpenAPI函数的输出将是对服务中REST方法的YAML格式化OpenAPI描述。

因此,如果我们使用浏览器打开URL:http://localhost:888/myserver/api,我们将获得完整的OpenAPI描述:

openapi: "3.0.0"
info:
title: SMARTDEMO
description: "HTTP smart service supp. FastCGI"
version: "1"
paths:
/myserver/api:
get:
operationId: get_myserver_api
responses:
"200":
content:
application/x-yaml:
schema:
type: string
description: "Success response"
/myserver/helloworld:
get:
operationId: get_myserver_helloworld
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/now1:
get:
operationId: get_myserver_now1
responses:
"200":
content:
text/plain:
schema:
type: string
format: date-time
description: "Success response"
/myserver/now2:
get:
operationId: get_myserver_now2
responses:
"200":
content:
text/plain:
schema:
type: number
format: double
description: "Success response"
/myserver/echostring/{AString}:
get:
operationId: get_myserver_echostring__AString_
parameters:
-
in: path
name: AString
required: true
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/myechostring/{AString}:
get:
operationId: get_myserver_myechostring__AString_
parameters:
-
in: path
name: AString
required: true
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echourl:
get:
operationId: get_myserver_echourl
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echoheader:
get:
operationId: get_myserver_echoheader
parameters:
-
in: header
name: Accept
required: true
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echoanyheader/{AHeaderName}:
get:
operationId: get_myserver_echoanyheader__AHeaderName_
parameters:
-
in: path
name: AHeaderName
required: true
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echocookie:
get:
operationId: get_myserver_echocookie
parameters:
-
in: cookie
name: MyCookie
required: true
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echoreversedstring:
post:
operationId: post_myserver_echoreversedstring
requestBody:
required: true
content:
text/plain:
schema:
type: string
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/myserver/echobytes:
post:
operationId: post_myserver_echobytes
requestBody:
required: true
content:
text/plain:
schema:
type: string
format: byte
responses:
"200":
content:
text/plain:
schema:
type: string
format: byte
description: "Success response"
/myserver/echoreversedconfigstring:
get:
operationId: get_myserver_echoreversedconfigstring
responses:
"200":
content:
text/plain:
schema:
type: string
description: "Success response"
/someabspath/addnumbers:
get:
summary: "Adds two numbers and returns result"
operationId: add_numbers
parameters:
-
in: query
name: arg1
required: true
description: "First numeric argument"
schema:
type: integer
format: int32
-
in: query
name: arg2
required: true
description: "Second numeric argument"
schema:
type: integer
format: int32
responses:
"200":
content:
text/plain:
schema:
type: integer
format: int32
description: "The result of the added numbers"
/myserver/storeperson:
post:
operationId: post_myserver_storeperson
requestBody:
required: true
content:
text/plain:
schema:
"$ref": "#/components/schemas/person"
responses:
"200":
content:
text/plain:
schema:
type: integer
format: int32
description: "Success response"
/myserver/getperson/{id}:
get:
operationId: get_myserver_getperson__id_
parameters:
-
in: path
name: id
required: true
schema:
type: integer
format: int32
responses:
"200":
content:
application/json:
schema:
"$ref": "#/components/schemas/person"
description: "Success response"
/myserver/getpersons:
get:
operationId: get_myserver_getpersons
responses:
"200":
content:
application/json:
schema:
type: array
items:
"$ref": "#/components/schemas/person"
description: "Success response"
components:
schemas:
person:
properties:
Name:
type: string
Address:
type: string
Age:
type: integer
format: int32
type: object
title: person

如果你愿意,可以随意学习。您可能会注意到有几个地方,有设施可以添加摘要和说明。例如,检查addnumbers调用:

  /someabspath/addnumbers:
get:
summary: "Adds two numbers and returns result"
operationId: add_numbers
parameters:
-
in: query
name: arg1
required: true
description: "First numeric argument"
schema:
type: integer
format: int32
-
in: query
name: arg2
required: true
description: "Second numeric argument"
schema:
type: integer
format: int32
responses:
"200":
content:
text/plain:
schema:
type: integer
format: int32
description: "The result of the added numbers"

摘要和描述来自哪里?

看一下Unit2.pas中AddNumbers函数的定义,很明显:

     // Add two numbers.
// It can be called from regular clients, smart clients
// and REST clients.
// It can be called from a browser like this:
// http://.../someabspath/addnumbers?arg1=10&arg2=20
[kbmMW_Method]
[kbmMW_Rest('method:get, path: "/someabspath/addnumbers", '+
'id:"add_numbers", '+
'summary:"Adds two numbers and returns result", '+
'resultDescription:"The result of the added numbers"')]
function AddNumbers([kbmMW_Rest('value: "$arg1", required: true, description:"First numeric argument"')] const AValue1:integer;
[kbmMW_Rest('value: "$arg2", required: true, description:"Second numeric argument"')] const AValue2:integer;
[kbmMW_Arg(mwatRemoteLocation)] const ARemoteLocation:string):integer;

kbmMW_Rest属性中甚至有一个id值。OpenAPI要求每个REST路径必须具有唯一ID。kbmMW将自动尝试生成一个,但您可以通过id语法选择自己的ID名称,如上所示。id,summary,description和resultDescription都是(以kbmMW为单位)全部可选。如果OpenAPI需要描述性值且未给出任何值,则kbmMW提供默认值。

所以现在我们可以生成有效的OpenAPI描述。我们如何让我们的基于kbmMW的应用服务器使用Swagger-UI呈现它们

因为kbmMW可以充当Web服务器,所以这实际上也很容易。我们只需将TkbmMWFilePool实例添加到主窗体(Unit1)并将服务数据模块(Unit2)的FilePool属性设置为指向它。

现在,kbmMW将作为常规Web服务器工作,并在未找到要调用的REST函数时尝试提供文件。

在与SimpleInvocation服务器可执行文件相同的目录中,您应该创建一个名为MyServer的目录(以匹配服务),在其下面,我们添加一个api目录,两者都只是为了匹配Unit2中OpenAPI函数的逻辑路径。实际上,您不必使用此特定路径层次结构,但我已选择此类演示。

在api目录中,我们将放置从https://swagger.io/tools/swagger-ui/下载的文件

然后

您可以将dist文件夹中的所有文件逐个下载到api \ dist目录,也可以通过克隆或下载按钮下载所有文件。如果您执行稍后操作,则打开下载的zip文件并将包含内容的dist文件夹解压缩到api目录。

最后将一个名为index.html的文件添加到api目录中。您可以从以下位置复制/粘贴其内容:

<!DOCTYPE html>
<!-- HTML for static distribution bundle build -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger Editor</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Roboto,sans-serif;
font-size: 9px;
line-height: 1.42857143;
color: #444;
margin: 0px;
} #swagger-editor {
font-size: 1.3em;
} .container {
height: 100%;
max-width: 880px;
margin-left: auto;
margin-right: auto;
} #editor-wrapper {
height: 100%;
border:1em solid #000;
border:none;
} .Pane2 {
overflow-y: scroll;
} </style>
<link href="./dist/swagger-editor.css" rel="stylesheet">
<link rel="icon" type="image/png" href="./dist/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./dist/favicon-16x16.png" sizes="16x16" />
</head> <body>
<div id="swagger-editor"></div>
<script src="./dist/swagger-editor-bundle.js"> </script>
<script src="./dist/swagger-editor-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const editor = SwaggerEditorBundle({
dom_id: '#swagger-editor',
layout: 'StandaloneLayout',
presets: [
SwaggerEditorStandalonePreset
]
}) window.editor = editor
}
</script> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol> <symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol> <symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol> <symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol> <symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol> <symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol> <symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol> </defs>
</svg> </body> </html>

现在你准备摇滚了。

启动服务器。然后启动浏览器并输入:

http://localhost:888/myserver/api/index.html?url=/myserver/api

这指示kbmMW提供index.html文件,该文件反过来请求生成Swagger-UI接口所需的剩余文件。最后,我们告诉Swagger-UI从/ myserver / api URL加载OpenAPI描述(如果你记得的话,将在Unit2中调用OpenAPI函数)。

您现在应该得到类似于此博客文章开头所示的视图。

在Swagger-UI界面中,您可以为kbmMW公开的所有REST功能生成服务器骨架和客户端存根。

快乐的摇摆!!!

如果你喜欢kbmMW,请分享这个词。转发博客文章,让其他人了解该产品!

https://components4developers.blog/2018/12/31/rest-easy-with-kbmmw-20-openapi/

REST easy with kbmMW #20 – OpenAPI and Swagger UI的更多相关文章

  1. REST easy with kbmMW #3 – SSL

    我在前两篇文章中展示了“REST easy with kbmMW”文章,如何使用kbmMW制作REST服务器,以及如何使用该REST服务器轻松地从数据库返回和存储数据,所有这些都在不到30行的真实数据 ...

  2. REST easy with kbmMW #14 – DB Controlled login

    介绍 关于如何使用授权和登录管理来构建应用服务器还存在一些问题,其中之一就是用户及其角色如何在在数据库中定义.该文将解释使用TkbmMWAuthorizationManager解决此问题的一种方法.有 ...

  3. Swagger UI in AspNetCore WebAPI

    Swagger其实包含了三个部分,分别是Swagger Editor文档接口编辑器,根据接口文档生成code的Swagger Codegen,以及生成在线文档的Swagger UI.在AspNetCo ...

  4. asp.net core 集成swagger ui

    什么是Swagger? 说swagger 之前,我们先说一下OpenApi 规范. OpenApi 是一种和语言无关的用于描述RESTAPIs 接口功能的一种规范,对RESTAPIs 接口的描述包括: ...

  5. .NET Core基础篇之:集成Swagger文档与自定义Swagger UI

    Swagger大家都不陌生,Swagger (OpenAPI) 是一个与编程语言无关的接口规范,用于描述项目中的 REST API.它的出现主要是节约了开发人员编写接口文档的时间,可以根据项目中的注释 ...

  6. ABP框架 - Swagger UI 集成

    文档目录 本节内容: 简介 Asp.net Core 安装 安装Nuget包 配置 测试 Asp.net 5.x 安装 安装Nuget包 配置 测试 简介 来自它的网页:“...使用一个Swagger ...

  7. ABP理论学习之Swagger UI集成

    返回总目录 本篇目录 介绍 安装 安装Nuget包 配置 测试 介绍 从官方网站上可以看到:"启用了Swagger API,就可以获得交互式文档,生成和发现客户端SDK". 安装 ...

  8. ASP.NET Core 在 Swagger UI 中显示自定义的 Header Token

    Swagger 是个好东西,对于前后端分离的网站来说,不仅是提高前后端开发人员沟通效率的利器,也大大方便了后端人员测试 API.有时候,API 中可能需要在 Header 中设置认证参数,比如 aut ...

  9. 在Abp中集成Swagger UI功能

    在Abp中集成Swagger UI功能 1.安装Swashbuckle.Core包 通过NuGet将Swashbuckle.Core包安装到WebApi项目(或Web项目)中. 2.为WebApi方法 ...

随机推荐

  1. 『TensorFlow』迁移学习

    完全版见github:TransforLearning 零.迁移学习 将一个领域的已经成熟的知识应用到其他的场景中称为迁移学习.用神经网络的角度来表述,就是一层层网络中每个节点的权重从一个训练好的网络 ...

  2. array 数组去重 过滤空值等方法

    去重操作 第一种方式, ES 6 引入的新书据结构 Set 本身就是没有重复数据的, 可以使用这个数据结构来转化数组.时间复杂度 O(n) 123456 const target = [];const ...

  3. PAT 1050 String Subtraction

    1050 String Subtraction (20 分)   Given two strings S​1​​ and S​2​​, S=S​1​​−S​2​​ is defined to be t ...

  4. SpringBoot利用注解@Value获取properties属性为null

    参考:https://www.cnblogs.com/zacky31/p/8609990.html 今天在项目中想使用@Value来获取Springboot中properties中属性值. 场景:定义 ...

  5. Spring注解之 @EnableScheduling计划任务注解

    要实现计划任务,首先通过在配置类注解@EnableScheduling来开启对计划任务的支持, 然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务 示例:计划任务执行类 在这个 ...

  6. ActiveMQ 集群和主从

    举例说明:假设有 3 个 broker 节点,分别是61616,61618, 61620,其中 61616 和 61618 组成主.从节点,而 61616(或61618)和 61620 构成集群.61 ...

  7. ActiveMQ异步分发消息

    org.apache.activemq.ActiveMQConnection 类中有个参数: protected boolean dispatchAsync=true; 这个参数的含义到底是什么? 使 ...

  8. Linux磁盘性能分析(CentOS)

    1.top查看CPU是否长时间等待IO top %wa超过30%,说明IO压力很大 2.iostat查看磁盘工作时长占比 iostat -x #1表示1秒刷新一次 %util表示在过去的时间段中磁盘进 ...

  9. JQuery对象和DOM对象的区别与转换

    刚开始学习JQuery,经常分不清楚哪些是JQuery对象,哪些是DOM对象,了解它们之间的关系是很有必要的. 1.DOM对象和JQuery对象的区别 1)  DOM对象 DOM是Document O ...

  10. Win10系列:C#应用控件基础3

    CheckBox控件 在应用程序的开发过程中开发者经常使用一组CheckBox控件来显示多个复选框,让用户从中选择一个或多个.当用户勾选复选框后,被选中的复选框会被标记为勾选状态,再次点击此复选框可取 ...