创建 REST 服务简介

2022-08-04 16:30:21 浏览数 (3)

本文介绍 IRIS® 中的 RESTREST 服务。

REST 简介

REST 命名自“Representational State Transfer”,具有以下属性:

  • REST 是一种架构风格,而不是一种格式。尽管 REST 经常使用 HTTP 来传输消息并使用 JSON 来传递数据,但也可以将数据作为 XML 或纯文本传递。 REST 利用现有的 Web 标准,例如 HTTPURLXMLJSON
  • REST 是面向资源的。通常,资源由 URL 标识并使用基于 HTTP 方法的操作,例如 GETPOSTPUTDELETE
  • REST 通常有少量开销。虽然它可以使用 XML 来描述数据,但它更常用的是 JSON,它是一种轻量级的数据包装器。 JSON 使用标签标识数据,但标签没有在正式的模式定义中指定,也没有明确的数据类型。

REST 服务简介

IRIS 2019.2 及更高版本中定义 REST 接口有两种方法:

  • 规范优先定义——首先创建一个 OpenAPI 2.0 规范,然后使用 API 管理工具生成 REST 接口的代码。
  • 手动编码 REST 接口。

使用规范优先的定义,REST 服务正式由以下组件组成:

  • 规范类(%REST.Spec 的子类)。此类包含 REST 服务的 OpenAPI 2.0 规范。 支持可以在规范中使用的几个扩展属性。
  • 调度类(%CSP.REST 的子类)。该类负责接收HTTP请求并调用实现类中合适的方法。
  • 一个实现类(%REST.Impl 的子类)。此类定义实现 REST 调用的方法。

API 管理工具生成实现类的存根版本,然后可以扩展它以包含必要的应用程序逻辑。 (逻辑当然可以调用此类之外的代码。)

%REST.Impl 类提供了可以调用的方法,以便设置 HTTP 标头、报告错误等。

Web 应用程序,通过 Web Gateway 提供对 REST 服务的访问。 Web 应用程序配置为启用 REST 访问并使用特定的调度类。 Web 应用程序还控制对 REST 服务的访问。

对这些组件遵循严格的命名约定。给定一个应用程序名称(appname),规范、调度和实现类的名称分别是 appname.specappname.dispappname.impl。 Web 应用程序默认命名为 /csp/appname,但可以使用其他名称。

支持规范优先范式。可以从规范生成初始代码,并且当规范发生变化时(例如,通过获取新的端点),可以重新生成该代码。后面的部分提供了更多细节,但现在,请注意,永远不应该编辑调度类,但可以修改其他类。此外,当重新编译规范类时,调度类会自动重新生成并更新实现类(保留编辑)。

手动编码 REST 服务

在 2019.2 之前的版本中,IRIS 不支持规范优先范式。一个 REST 服务形式上只包含一个调度类和一个 Web 应用程序。引用这种方式将 REST 服务定义为手动编码的 REST 服务。区别在于较新的 REST 服务定义的 REST 服务包含规范类,而手动编码的 REST 服务不包含。本书的“手动创建 REST 服务”附录描述了如何使用手动编码范例创建 REST 服务。同样,一些 API 管理实用程序使您能够使用手动编码的 REST 服务。

API 管理工具简介

为了帮助更轻松地创建 REST 服务, 提供了以下 API 管理工具:

  • 一个名为 /api/mgmntREST 服务,可以使用它来发现服务器上的 REST 服务,为这些 REST 服务生成 OpenAPI 2.0 规范,以及在服务器上创建、更新或删除 REST 服务。
  • ^%REST 例程,它提供了一个简单的命令行界面,可以使用它来列出、创建和删除 REST 服务。
  • %REST.API 类,可以使用它来发现服务器上的 REST 服务,为这些 REST 服务生成 OpenAPI 2.0 规范,以及在服务器上创建、更新或删除 REST 服务。

可以为这些工具设置日志记录,如本章后面所述。

有用的第三方工具包括 REST 测试工具,例如 PostMan (https://www.getpostman.com/) 和 Swagger 编辑器 (https://swagger.io/tools/swagger-editor/download/)。

创建 REST 服务概述

创建 REST 服务的推荐方式大致如下:

  1. 获取(或编写)服务的 OpenAPI 2.0 规范。
  2. 使用 API 管理工具生成 REST 服务类和关联的 Web 应用程序。请参阅以下章节:
  • “使用 /api/mgmnt/ 服务创建 REST 服务”
  • “使用 ^%REST 例程创建 REST 服务”
  • “使用 %REST.API 类创建 REST 服务”
  1. 修改实现类,使方法包含合适的业务逻辑。请参阅“修改实现类”一章。
  2. 可以选择修改规范类。请参阅“修改规范类”一章。例如,如果需要支持 CORS 或使用 Web 会话,请执行此操作。
  3. 如果需要安全性,请参阅“保护 REST 服务”一章。
  4. 使用服务的 OpenAPI 2.0 规范,生成文档,如“发现和记录 REST API”一章中所述。

对于第 2 步,另一种选择是手动创建规范类(将规范粘贴到其中),然后编译该类;此过程生成调度和存根实现类。也就是说,使用 /api/mgmnt 服务或 ^%REST 例程都不是绝对必要的。本书没有进一步讨论这种技术。

详细了解 REST 服务类

本节详细介绍了规范、调度和实现类。

Specification Class

规范类旨在定义 REST 服务要遵循的契约。此类扩展 %REST.Spec 并包含一个 XData 块,该块包含 REST 服务的 OpenAPI 2.0 规范。下面显示了一个部分示例:

代码语言:javascript复制
Class YX.SPEC Extends %REST.Spec
{

XData OpenAPI [ MimeType = application/json ]
{
    {
      "swagger":"2.0",
      "info":{
        "version":"1.0.0",
        "title":"Swagger Petstore",
        "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
        "termsOfService":"http://swagger.io/terms/",
        "contact":{
          "name":"Swagger API Team"
        },
        "license":{
          "name":"MIT"
        }
      }
    }
}

}

可以通过替换或编辑 XData 块中的规范来修改此类。还可以根据需要添加类参数、属性和方法。每当编译规范类时,编译器都会重新生成调度类并更新实现类。

Dispatch Class

调用 REST 服务时直接调用调度类。下面显示了一个部分示例:

代码语言:javascript复制
Class YX.DISP Extends %CSP.REST [ GeneratedBy = YX.SPEC.cls, ProcedureBlock ]
{

/// The class containing the RESTSpec which generated this class
Parameter SpecificationClass = "petstore.spec";

/// Default the Content-Type for this application.
Parameter CONTENTTYPE = "application/json";

/// By default convert the input stream to Unicode
Parameter CONVERTINPUTSTREAM = 1;

/// The default response charset is utf-8
Parameter CHARSET = "utf-8";

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
  <Route Url="/pets" Method="get" Call="findPets" />
  <Route Url="/pets" Method="post" Call="addPet" />
  <Route Url="/pets/:id" Method="get" Call="findPetById" />
  <Route Url="/pets/:id" Method="delete" Call="deletePet" />
</Routes>
}

}

请注意,SpecificationClass 参数指示相关规范类的名称。 URLMap XData 块(URL 映射)定义此 REST 服务中的调用。

在这些项目之后,该类包含 URL 映射中列出的方法的定义。这是一个例子:

代码语言:javascript复制
ClassMethod deletePet(pid As %String) As %Status
{
    Try {
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        If ($number(pid,"I")="") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP400BADREQUEST,$$$ERROR($$$RESTInvalid,"id",id)) Quit
        Set response=##class(petstore.impl).deletePet(pid)
        Do ##class(petstore.impl).%WriteResponse(response)
    } Catch (ex) {
        Do ##class(%REST.Impl).%ReportRESTError(..#HTTP500INTERNALSERVERERROR,ex.AsStatus())
    }
    Quit $$$OK
}

请注意以下几点:

  • 此方法调用实现类中的同名方法(本例中为 petstore.impl)。它从该方法获取响应并调用 %WriteResponse() 将响应写回调用者。 %WriteResponse() 方法是一种继承方法,存在于所有实现类中,这些实现类都是 %REST.Impl 的子类。
  • 此方法进行其他检查,并在出现错误时调用 %REST.Impl 的其他方法。

重要提示:因为调度类是一个生成的类,你永远不应该编辑它。 提供了覆盖部分调度类而不对其进行编辑的机制。

Implementation Class

实现类旨在保存 REST 服务的实际内部实现。可以(并且应该)编辑此类。它最初类似于以下示例:

代码语言:javascript复制
/// A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification<br/>
/// Business logic class defined by RESTSpec in petstore.spec<br/>
Class petstore.impl Extends %REST.Impl [ ProcedureBlock ]
{

/// If ExposeServerExceptions is true, then details of internal errors will be exposed.
Parameter ExposeServerExceptions = 0;

/// Returns all pets from the system that the user has access to<br/>
/// The method arguments hold values for:<br/>
///     tags, tags to filter by<br/>
///     limit, maximum number of results to return<br/>
ClassMethod findPets(tags As %ListOfDataTypes(ELEMENTTYPE="%String"), limit As %Integer) As %Stream.Object
{
    //(Place business logic here)
    //Do ..%SetStatusCode(<HTTP_status_code>)
    //Do ..%SetHeader(<name>,<value>)
    //Quit (Place response here) ; response may be a string, stream or dynamic object
}

...

实现类的其余部分包含与此类似的附加存根方法。在每种情况下,这些存根方法都具有遵循 REST 服务规范定义的契约的签名。请注意,对于 options 方法, 不会生成存根方法供实现。相反,%CSP.REST 类会自动执行所有选项处理。

为 API 管理功能启用日志记录

要启用 API 管理功能的日志记录,请在终端中输入以下内容:

代码语言:javascript复制
 set $namespace="%SYS"
 kill ^ISCLOG
 set ^%ISCLOG=5
 set ^%ISCLOG("Category","apimgmnt")=5

然后系统将条目添加到 ^ISCLOG 全局,以用于对 API 管理端点的任何调用。

要将日志写入文件(为了便于阅读),请输入以下内容(仍在 %SYS 命名空间内):

代码语言:javascript复制
 do ##class(%OAuth2.Utils).DisplayLog("filename")

其中 filename 是要创建的文件的名称。该目录必须已经存在。如果文件已存在,则将其覆盖。

要停止记录,请输入以下内容(仍在 %SYS 命名空间中):

代码语言:javascript复制
 set ^%ISCLOG=0
 set ^%ISCLOG("Category","apimgmnt")=0

查看日志

启用 HTTP 请求的日志记录后,日志条目将存储在 ^ISCLOG 全局中,该全局位于 %SYS 命名空间中。

要使用管理门户查看日志,请导航到 System Explorer > Globals 并查看 ISCLOG 全局(不是 %ISCLOG)。确保位于 %SYS 命名空间中。

0 人点赞