ebredy / apichainning

API 链接

1.1.9 2019-06-24 13:03 UTC

This package is auto-updated.

Last update: 2024-09-25 07:51:08 UTC


README

目前处于开发中。

要获取更多关于使用模拟 API 链接的示例,请访问 http://api-multi-request.demos.mikestowe.com/

REST API 多请求链接

在使用由超媒体驱动的 RESTful API 以及引入众多和广泛的微服务时,一个挑战是有时需要执行多个 API 调用来完成手头的任务。今天,这也需要许多 HTTP 调用,这取决于延迟,可能会极大地减慢脚本执行。

REST API 多请求链接是一种技术,它将多个 RESTful API 调用组合在一个单一的 HTTP 请求中。

例如,如下所示,您不必执行对 /users/5/messages/?userId=5 的 GET 调用,而可以向类似 /multirequestchain 的资源发送链,该资源将为您处理多个调用。

REST API 多请求链接配置为允许条件调用,并提供所需的数据(而不是每个调用返回的所有数据)。这也可以用作类似于 GraphQL 的数据收集技术,在那里,您不是将对象作为模型嵌入,而是能够一次性执行多个调用并只获取请求的数据。

简单的 REST API 多请求链接

每个链接由5个组件组成,都是必需的

一个简单的请求,您想获取一个用户的消息,但首先需要调用 /users 资源以获取信息,可能看起来像这样

[
  {
    "doOn": "always",
    "url": "/users/5",
    "method": "get",
    "data": {},
    "return": "$.*._links"
  },
  {
    "doOn": 200,
    "url" : "$body._links.messages",
    "method": "get",
    "data": {
        "emailAddress": "$body.email"
    },
    "headers": {
        "Cache-Control": "no-cache",
        "Connection": "keep-alive"
    }
    "return": true,
  }
]

多个条件调用

REST API 多请求链接按时间顺序工作,每个调用都被视为下一个最高优先级或下一个自然步骤。

doOn 指定是否基于前一个调用的响应执行调用,或者是否跳过包含匹配前一个 HTTP 状态码的 doOn 的另一个调用,应用返回 true 的逻辑 IF 语句,或指定为 "always"。如果在当前操作链层的层面上找不到合适的匹配项,则链将认为这是一个错误并退出,返回所有数据以及包括最后一个尝试的调用。

例如,在我们的上一个链中,如果 /users/5 调用返回 404,则链将退出,为您提供该调用的详细信息,但 尝试下一个调用 $body._links.messages,因为它需要一个状态码为 200。

您还可以通过将它们放在数组中分层多个条件调用,创建一个新层。但是,一旦链达到其子层的末尾,它将退出 - 遍历剩余的父层。

[
  {
    "doOn": "always",
    "url": "/users/5",
    "method": "get",
    "data": {},
    "return": [
        "email", "_links"
    ]
  },
  [
    {
      "doOn": 200,
      "url" : "$body._links.messages",
      "method": "get",
      "data": {
          "emailAddress": "$body.email"
      },
      "return": true,
    },
    {
      "doOn": "4*|5*",
      "url" : "/users",
      "method": "post",
      "data": {
        "firstName": "Jim",
        "lastName": "Smith",
        "emailAddress": "jim.smith@domain.ext"
      },
      "return": true,
    },
    [
      {
        "doOn": "201",
        "url": "$headers.link",
        "method": "get",
        "data": {},
        "return": [
            "email", "_links"
        ]
      },
      {
        "doOn": 200,
        "url" : "$body._links.sendMessage",
        "method": "post",
        "data": {
          "to": "$body.email",
          "subject": "Welcome $body.firstName",
          "body": "Hello and welcome to our site!"
        },
        "return": true,
      }
    ]
  ]
]

复杂的 IF 语句

doOn 属性接受 HTTP 状态码、字符串 "always" 或条件逻辑 IF 语句

简单的等于/不等于
AND OR 语句
正则表达式

复杂示例 1

[
  {
    "doOn": "always",
    "url": "/users/5",
    "method": "get",
    "data": {},
    "return": [
        "firstName", "lastName", "email", "_links"
    ]
  },
  {
    "doOn": "($body.firstName == "Jim" && $body.lastName == "Smith") || regex('/Jim/i', $body.email)",
    "url" : "$body._links.messages",
    "method": "get",
    "data": {
        "emailAddress": "$body.email"
    },
    "return": true,
  }
]

复杂示例 2

[
        {
            "doOn": "always",
            "url": "https://www.tn-apis.com/catalog/v1/events/${global.eventId}",
            "method": "get",
            "name": "api:ticketnetwork:catalog:events",
            "globals": {},
            "data": {},
            "headers": {
                "Authorization": "Bearer 9750bc1c-178d-36f7-802b-b6b9ebcb3efd",
                "x-listing-context": "website-config-id=3551",
                "Accept": "application/json"
            },
            "return": {
                "latitudeFromTN": "geoLocation.latitude",
                "longitudeFromTN": "geoLocation.longitude",
                // custom php callbacks are created with 
                //and called by prefixing them with callback.  An addition any php function 
                //can be called using the prefix "callback_"
                "dateFromTN": "${callback_extract_date(date.datetime,0,10)}",  
                "timeFromTN": "${callback_format_time(date.text.time)}",
                "venueId": "venue.id",
                "countryFromTN": "country.alphaCode",
                "stateFromTN": "stateProvince.text.abbr",
                "eventNameFromTN": "text.name",
                "cityFromTN": "city.text.name",
                "isResult": "${callback_has_results(venue.id)}"
            }
        },
        {
            "doOn": "200",
            "url": "https://www.way.com/way-service/home/suggestions",
            "method": "post",
            "name": "api:way:suggestions",
            "data": {
                "latitude": "$body.latitudeFromTN",
                "longitude": "$body.longitudeFromTN",
                "searchQuery": "TNvenue${body.venueId}WAY ${body.dateFromTN} ${body.timeFromTN}",
                "serviceType": "PARKING"
            },
            "headers": {
                "Content-Type": "application/json"
            },
            "return": {
                "wayEventId": "response[0].listingIdentifier",
                "wayVenueId": "response[0].venueId",
                "wayLatitude": "response[0].latitude",
                "wayLongitude": "response[0].longitude",
                "wayAddressLine1": "response[0].addressBo.addressLine1",
                "wayCity": "response[0].addressBo.city.cityName",
                "wayState": "response[0].addressBo.state.stateCode",
                "wayZipcode": "response[0].addressBo.zipcode.zipcode",
                "wayCountry": "response[0].addressBo.country.countryName",
                "wayHasResult": "${callback_has_results(response)}"
            }
        },
        {
            "doOn": "$body.wayHasResult == true",
            "url": "https://www.way.com/way-service/parking/search",
            "method": "post",
            "name": "api:way:parking:search:events",
            "data": {
                "eventId": "$body.wayEventId",
                "reqType": "jump",
                "venueId": "$body.wayVenueId",
                "latitude": "$body.wayLatitude",
                "longitude": "$body.wayLongitude",
                "pageIndex": 1,
                "parkingFilter": {
                    "nearBy": "Event"
                },
                "numberOfRecords": 10
            },
            "headers": {
                "Content-Type": "application/json"
            },
            "return": true
        }
]

复杂示例 3(使用头/接受:application/xml)

 [
        {
            "doOn": "always",
            "url": "https://link-search.api.cj.com/v2/link-search?website-id=9007016&link-type=banner&advertiser-ids=joined",
            "method": "get",
            "name": "api:cjmanual:v2:linksearch",
            "globals": {},
            "data": {},
            "headers": {
                "Authorization": "Bearer 351q6ebydq98pwv1fttxcgpsqs",
                "Accept": "application/xml"
            },
            "return": true
        }
    ]

响应

由于使用 REST API 多请求链接时可能出现条件链接和错误,因此响应对象需要返回三个主要属性

在响应数组中,每个调用对象需要包括

{
  "callsRequested" : 2,
  "callsCompleted" : 2,
  "responses" : [
    {
      "url" : "/users/5",
      "method" : "get",
      "status" : 200,
      "response" : {
        "headers" : {},
        "body" : {
          "email": "user@user.ext",
          "_links": {}
        }
      }
    },

    {
      "url" : "/messages/?userId=5",
      "method" : "get",
      "status" : 200,
      "response" : {
        "headers" : {},
        "body" : {}
      }
    }
  ]
}

常见问题解答

如何在 REST API 多请求链接中发送头信息?

头信息将与以往一样发送,并将自动应用于链中请求的每个调用。在此期间,REST API 多请求链不支持为每个调用独立发送特定头信息的能力——然而,如果需要,这肯定可以在未来添加。

这与 IO 状态驱动 API 有何不同?

REST API 多请求链是专门为使用超媒体在静态或缓存状态文件上运行的 RESTful API 设计的。这使得可用路径可以真正地根据链内单个调用而动态变化,并且也防止了需要多次请求来获取 IO 状态或可用 OPTIONS。REST API 多请求链还旨在超越仅使用主键的使用,让您在执行下一个链中的操作时,可以调用上一个调用中的头信息和正文信息。每个链接/调用也是条件性的,这意味着您可以指定何时调用该链接,并在链接/调用失败时收到适当的错误响应。更多内容,您可以查看 Owen 的 IO 状态实现 这里