Go RESTAPI 开发入门详解

web 服务是定义在不同计算机系统之间的通信机制。如果没有 web 服务,定制的点对点通信将变得繁琐且特定于平台。网络需要理解和解释的东西有上百种。如果计算机系统与网络容易理解的协议相一致,这将是一个很大的帮助。

web 服务是一种软件系统,旨在通过网络支持可互操作的机器对机器交互,万维网联盟W3C),https://www.w3.org/TR/ws-arch/

现在,简单地说,web 服务是两个端点之间的一条通道,在这两个端点之间消息可以顺利传输。在这里,这种转移通常是一种方式。两个单独的可编程实体也可以通过各自的 API 相互通信。两个人通过语言交流。两个应用程序通过应用程序编程接口API进行通信。

读者可能会疑惑;API 在当前数字世界中的重要性是什么?物联网物联网的兴起使得 API 的使用量比以前更大。人们对 API 的认识与日俱增,世界各地每天都在开发和记录数百种 API。值得注意的主要业务是在API 中将期货视为一项服务AAAS)。一个很好的例子是亚马逊网络服务AWS)。这在云世界是一个巨大的成功。开发人员使用 AWS 提供的 RESTAPI 编写自己的应用程序

还有一些隐藏的用例来自于像 Ibibo 和 Expedia 这样的旅游网站,它们通过调用第三方网关和数据供应商的 API 来获取实时价格。如今,Web 服务经常收费。

本章涉及的主题包括:

有许多类型的 web 服务随着时间的推移而不断发展。突出的是:

  • 肥皂
  • UDDI
  • WSDL
  • 休息

其中,SOAP在 21 世纪初开始流行,当时 XML 是最流行的。XML 数据格式被各种分布式系统用来相互通信。SOAP 太复杂,无法实现。SOAP 的批评者指出 SOAP HTTP 请求是多么庞大。

SOAP 请求通常由以下三个基本组件组成:

  • 信封
  • 标题
  • 身体

为了执行 HTTP 请求和响应周期,我们必须在 SOAP 中附加大量额外的数据。示例 SOAP 请求如下所示:

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"

<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <SOAP-ENV:Body>
       <m:GetLastTradePrice xmlns:m="Some-URI">
           <symbol>DIS</symbol>
       </m:GetLastTradePrice>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

这是 W3C 标准(中 SOAP 的标准示例 https://www.w3.org/TR/2000/NOTE-SOAP-20000508/ )。如果我们仔细观察,它是 XML 格式的,带有指定信封和正文的特殊标记。由于 XML 在许多名称空间上运行,因此附加信息起作用

加利福尼亚大学的 Roy Fielding 创造了代表性的状态转移(TytT2)。与 SOAP 相比,它是一个非常简化和轻量级的 web 服务。性能、可扩展性、简单性、可移植性和可修改性是 REST 设计背后的主要原则

RESTAPI 允许不同的系统以非常简单的方式进行通信和发送/接收数据。每个 RESTAPI 调用都有一个 HTTP 谓词和 URL 之间的关系。应用程序中数据库中的资源可以映射到 REST 中的 API 端点。

当您在手机上使用移动应用程序时,您的手机可能会秘密与许多云服务通话,以检索、更新或删除您的数据。休息服务对我们的日常生活有着巨大的影响。

REST 是一种无状态、可缓存且简单的体系结构,它不是一种协议,而是一种模式。

与以前的版本相比,这些主要特性使 REST 变得简单和独特:

  • 基于客户端-服务器的体系结构:这种体系结构对于现代 web 通过 HTTP 进行通信是最基本的。一个单一的客户端-服务器最初可能看起来很幼稚,但许多混合体系结构正在发展。我们不久将讨论更多这些问题。
  • 无状态:这是 REST 服务最重要的特性。RESTHTTP 请求包含服务器理解和返回响应所需的所有数据。一旦请求被送达,服务器就不记得请求是否在一段时间后到达。因此,该操作将是无状态的。
  • 可缓存:许多开发人员认为技术堆栈正在阻止他们的 web 应用程序或 API。但事实上,他们的建筑才是原因。数据库可能是 web 应用程序中的一个潜在调优部件。为了更好地扩展应用程序,我们需要缓存内容并将其作为响应交付。如果缓存无效,我们有责任破坏它。应正确缓存 REST 服务以进行扩展。
  • 按需脚本:您是否曾经设计过一个 REST 服务,为 JavaScript 文件提供服务,并动态执行它们?这种随需应变的代码也是 REST 可以提供的主要特性。从服务器请求脚本和数据更为常见。
  • 多层系统:REST API 可以从多台服务器提供服务。一台服务器可以请求另一台,以此类推。因此,当一个请求来自客户端时,可以在多个服务器之间传递请求和响应,以最终向客户端提供响应。这种易于实现的多层系统始终是保持 web 应用程序松散耦合的好策略。
  • 资源表示:REST API 提供统一的接口进行对话。它使用统一资源标识符URI来映射资源(数据)。它还具有请求特定数据格式作为响应的优点。Internet 媒体类型(MIME 类型)可以告诉服务器请求的资源属于该特定类型。 * 实现自由:REST 只是一种定义 web 服务的机制。它是一种可以以多种方式实现的体系结构样式。由于这种灵活性,您可以按照希望的方式创建 REST 服务。在遵循 REST 原则之前,您的服务器可以自由选择平台或技术。

****深思熟虑的缓存对于 REST 服务的扩展至关重要。

REST 谓词指定对特定资源或资源集合执行的操作。当客户端发出请求时,它应在 HTTP 请求中发送以下信息:

  • 休息动词
  • 标题信息
  • 主体(可选)

正如我们前面提到的,REST 使用 URI 来解码要处理的资源。详情如下:

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
  • OPTIONS

如果你是一名软件开发人员,你大部分时间都要处理这六个问题。下表说明了操作、目标资源以及请求成功或失败时的情况:

| 休息动词 | 动作 | 成功 | 故障 | | GET | 从服务器获取一条记录或一组资源 | 200 | 404 | | OPTIONS | 获取所有可用的 REST 操作 | 200 | - | | POST | 创建一组新的资源或资源 | 201 | 404, 409 | | PUT | 更新或替换给定记录 | 200, 204 | 404 | | PATCH | 修改给定的记录 | 200, 204 | 404 | | DELETE | 删除给定的资源 | 200 | 404 |

上表成功失败列中的数字为 HTTP 状态码。每当客户端启动 REST 操作时,由于 REST 是无状态的,客户端应该知道一种方法来确定操作是否成功。因此,HTTP 具有响应的状态代码。REST 为给定操作定义前面的状态代码类型。这意味着 RESTAPI 应该严格遵循前面的规则来实现客户端-服务器通信。

所有定义的 REST 服务都具有以下格式。它由主机和 API 端点组成。API 端点是服务器预定义的 URL 路径。每个 REST 请求都应该符合该路径。

一个简单的 RESTAPI URI:http://HostName/API endpoint/Query(optional)

让我们更详细地看一下所有的动词。RESTAPI 设计从定义操作和 API 端点开始。在实现 API 之前,设计文档应该列出给定资源的所有端点。在下一节中,我们将使用 PayPal 的 RESTAPI 作为用例仔细观察 RESTAPI 端点。

GET方法从服务器获取给定的资源。为了指定资源,GET使用几种类型的 URI 查询:

  • 查询参数
  • 基于路径的参数

如果您不知道,您对 web 的所有浏览都是通过向服务器执行GET请求来完成的。例如,如果您键入www.google.com,您实际上是在发出GET请求以获取搜索页面。在这里,您的浏览器是客户端,而 Google 的 web 服务器是 web 服务的后端实现者。成功的GET操作返回 200 状态码。

路径参数示例:

大家都知道贝宝。PayPal 与公司签订账单协议。如果您向 PayPal 注册支付系统,他们将为您提供一个 REST API,以满足您所有的计费需求。获取账单协议信息的样本GET请求如下所示:/v1/payments/billing-agreements/agreement_id

这里,资源查询使用 path 参数。当服务器看到这一行时,它将其解释为我收到了一个 HTTP 请求,需要来自计费协议的协议 id。然后它搜索数据库,进入billing-agreements表,找到与给定agreement_id的协议。如果该资源存在,它将发送详细信息以复制回响应(200 OK)。或者,它发送一个响应,表示未找到资源(404)。

使用GET,您还可以查询资源列表,而不是像前面的示例那样查询单个资源。PayPal 用于获取与协议相关的账单交易的 API 可以通过/v1/payments/billing-agreements/transactions获取。此行获取该账单协议上发生的所有交易。在这两种情况下,案例数据都是以 JSON 响应的形式检索的。响应格式应事先设计,以便客户可以在协议中使用。

查询参数的示例如下:

  • 查询参数用于添加详细信息,以从服务器中标识资源。例如,以这个虚构的 API 为例,假设这个 API 是为获取、创建和更新书籍的详细信息而创建的。基于GET请求的查询参数将采用以下格式:

**```go /v1/books/?category=fiction&publish_date=2017


*   前面的 URI 几乎没有查询参数。URI 正在从满足以下条件的图书资源请求图书:
    *   它应该是一本小说
    *   这本书应该在 2017 年出版

*获取 2017 年发布的所有小说*是客户端向服务器提出的问题

路径与查询参数何时使用?通常的经验法则是使用`Query`参数根据查询参数获取多个资源。如果客户端需要具有确切 URI 信息的单个资源,则可以使用`Path`参数指定该资源。例如,可以使用`Path`参数请求用户仪表板,并且可以使用`Query`参数对过滤数据进行建模。

在`GET`请求中,对单个资源使用`Path`参数,对多个资源使用`Query`参数。

# POST、PUT 和 PATCH

`POST`方法用于在服务器上创建资源。在上一本书的 API 中,此操作将创建具有给定详细信息的新书。成功的`POST`操作返回 201 状态码。`POST`请求可以更新多个资源:`/v1/books`。

`POST`请求的主体如下:

```go
{"name" : "Lord of the rings", "year": 1954, "author" : "J. R. R. Tolkien"}

这实际上在数据库中创建了一本新书。为该记录分配了一个 ID,以便在我们GET资源时,创建 URL。所以POST一开始只能做一次。事实上,指环王出版于 1955 年。因此,我们输入的发布日期不正确。为了更新资源,让我们使用PUT请求。

PUT方法与POST.类似,用于替换已经存在的资源。主要区别在于PUT是幂等的。一个POST调用创建两个具有相同数据的实例。但是PUT更新了一个已经存在的资源:

/v1/books/1256

主体是 JSON,如下所示:

{"name" : "Lord of the rings", "year": 1955, "author" : "J. R. R. Tolkien"}

1256是这本书的 ID。它通过year:1955更新前一本书。您是否注意到了PUT的缺点?它实际上用新唱片替换了整个旧唱片。我们需要更改单个列。但是PUT取代了整个记录。这很糟糕。为此,引入了PATCH请求

PATCH方法与PUT类似,只是不会替换整个记录。顾名思义,PATCH对正在修改的列进行了修补。让我们用一个名为ISBN的新专栏来更新这本书1256

/v1/books/1256

JSON 主体如下所示:

{"isbn" : "0618640150"}

它告诉服务器,搜索 id 为 1256 的书。然后使用给定值添加/修改此列。

PUT and PATCH both return the 200 status for success and 404 for not found.

DELETEAPI 方法用于从数据库中删除资源。与PUT相似,但没有任何主体。它只需要一个要删除的资源 ID。一旦资源被删除,后续的GET请求将返回 404 未找到状态

对该方法的响应是不可缓存(如果实现了缓存),因为DELETE方法是幂等的。

**OPTIONSAPI 方法是 API 开发中最被低估的方法。给定资源,此方法尝试了解服务器上定义的所有可能方法(GETPOST等)。这就像在餐馆里看菜单卡,然后点一道菜(如果你随意点了一道菜,服务员会告诉你没有)。在服务器上实现OPTIONS方法是最佳实践。从客户端,确保首先调用了OPTIONS,如果该方法可用,则继续该方法

OPTIONS方法最重要的应用是跨源资源共享CORS。最初,浏览器安全性阻止客户端发出跨源请求。这意味着加载了 URLwww.foo.com的站点只能对该主机进行 API 调用。如果客户端代码需要从www.bar.com请求文件或数据,那么第二台服务器bar.com应该有识别foo.com的机制来获取其资源。

该过程解释了 CORS:

  1. foo.combar.com上请求OPTIONS方法。
  2. bar.com向客户端发送类似Access-Control-Allow-Origin: http://foo.com的头信息。
  3. 接下来,foo.com可以访问bar.com上的资源,无需任何调用REST方法的限制。

如果bar.com在一个初始请求后想要向任何主机提供资源,它可以将访问控制设置为*(即任意)。

下图描述了一个接一个发生的过程:

有几个家庭的身份代码。每个族全局解释一个操作状态。这个家庭的每个成员都可能有一次更深入的会面。因此 restapi 应该严格地告诉客户端操作后到底发生了什么。有 60 多个状态代码可用。但对于其余部分,我们将重点关注几个代码族。

200 和 201 属于成功家族。它们表明手术是成功的。普通200操作成功是成功的积垢操作:

  • 200成功操作)是 REST 中最常见的响应状态码类型
  • POST操作在服务器上成功创建资源时,返回201成功创建) * 204无内容)在客户端需要状态但没有数据返回时发出

**# 3xx 系列(重定向)

这些状态代码用于传递重定向消息。最重要的是301304

* 当资源永久移动到新的 URL 端点时,发出301**。当旧 API 被弃用时,这一点至关重要。它在响应中返回状态为 301 的新端点。看到这一点,客户端应该使用新的 URL 来响应实现其目标。

  • 304状态码表示内容已缓存,服务器上的资源未发生修改。这有助于在客户端缓存内容,并且仅在修改缓存时请求数据

这些是客户端需要解释和处理进一步操作的标准错误状态代码。这些与服务器无关。错误的请求格式或格式错误的 REST 方法可能导致这些错误。其中,API 开发者最常用的状态代码是400401403404405

  • 当服务器无法理解客户端请求时,返回400坏请求)。
  • 客户端未发送表头授权信息时返回401未授权)。
  • 当客户端无法访问某类资源时返回403禁止)。
  • 当客户端请求位于不存在的资源上时,返回404未找到)。
  • 如果服务器禁止资源上的一些方法,则返回405方法不允许)。GETHEAD为例外情况。

这些是来自服务器的错误。客户端请求可能是完美的,但由于服务器代码中的错误,可能会出现这些错误。常用状态码为500501502503504

  • 500内部服务器错误)状态码给出了一些错误代码或一些意外情况导致的开发错误
  • 当服务器不再支持资源上的方法时,返回501未实现
  • 当服务器自身收到另一个服务供应商的错误响应时,返回502坏网关
  • 503服务不可用)在服务器因负载过重或维护等多种原因停机时返回
  • 当服务器等待另一个供应商的响应时间过长且服务客户端的时间过长时,返回504网关超时

有关状态代码的更多详细信息,请访问此链接:https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

您需要了解为什么单页应用程序SPA)是今天的热门话题。这些 SPA 设计使开发人员以完全不同的方式编写代码,而不是以传统的方式构建 UI(请求网页)。有许多 MVC 框架,如 AngularJS、Angular2、React JS、Knockout JS、Aurelia 等,可以快速开发 web UI,但它们的本质都非常简单。所有 MVC 框架都帮助我们实现一种设计模式。该设计模式没有对网页的请求,只有 RESTAPI。

自 2010 年以来,现代 web 前端开发取得了很大进步。为了利用 AutoT0}模型视图控制器 ALE T1(MultT2 MVC OutT3)体系结构的特点,我们需要考虑前端是一个单独的实体,它只使用 REST API(最优选的是 REST JSON)与后端进行对话。

所有网站都要经过以下步骤:

  1. 从服务器请求网页。
  2. 验证并显示仪表板 UI。
  3. 允许用户修改和保存。
  4. 根据需要从服务器请求尽可能多的网页,以显示站点上的各个页面。

但在水疗中心,水流是完全不同的:

  1. 一次性向浏览器请求 HTML 模板。
  2. 然后,查询 JSON REST API 以填充模型(数据对象)。
  3. 根据模型中的数据(JSON)调整 UI。
  4. 当用户修改 UI 时,模型(数据对象)应自动更改。例如,在 AngularJS 中,可以使用双向数据绑定。最后,随时调用 RESTAPI 通知服务器更改。

这样,通信只以 RESTAPI 的形式发生。客户端负责逻辑地表示数据。这导致系统从面向响应的架构ROA)转向面向服务的架构SOA)。请看下图:

SPA 减少了带宽并提高了站点性能。

REST 服务在现代 web 中是微不足道的。SOA(稍后我们将更详细地讨论)为 REST 服务创建了一个活动空间,将 web 开发提升到一个新的水平。Go是来自 Google 之家的一种编程语言,用于解决他们面临的更大问题。它自第一次出现以来已经八年多了。随着开发人员社区的加入,它逐渐成熟,并在其中创建了大规模的系统。

围棋是网络的宠儿。它以一种简单的方式解决更大的问题。

可以选择 Python 或 JavaScript(节点)进行 RESTAPI 开发。Go 的主要优点在于它的速度和编译时错误检测。通过各种基准测试,Go 在计算性能方面被证明比动态编程语言更快。以下是公司应该在 Go 中编写下一个 API 的三个原因:

  • 为更广泛的受众扩展 API
  • 使开发人员能够构建健壮的系统
  • 投资于项目未来的可行性

您可以查看无休止的在线辩论,以了解有关使用 Go 的 REST 服务的更多信息。在后面的章节中,我们将尝试构建设计和编写 REST 服务的基础。

这是一本建筑系列书。它假设你已经知道围棋的基本知识。如果没有,不用担心。您可以从 Go 官方网站快速入门并学习 https://golang.org/ 。Go 使用不同的方式开发项目。编写一个独立的、简单的程序不会给你带来太多麻烦。但是,在学习了基础知识之后,人们试图更进一步。因此,作为一名 Go 开发人员,您应该知道 Go 项目是如何布局的,以及保持代码整洁的最佳实践。

在继续之前,请确保已完成以下操作:

  • 在您的计算机上安装 Go 编译器
  • 设置GOROOTGOPATH环境变量

有许多在线参考资料,您可以从中了解前面的详细信息。根据您的机器类型(Windows、Linux 或 macOS X),设置一个运行的 Go 编译器。关于GOPATH的更多细节,请参见下一节。

GOPATH只是您机器上当前指定的工作区。它是一个环境变量,告诉 Go 编译器源代码、二进制文件和包的放置位置。

来自 Python 背景的程序员可能知道 Virtualenv 工具,可以同时创建多个项目(使用不同的 Python 解释器版本)。但在给定的时间,一个人激活环境并开发他的项目。类似地,您的机器上可以有任意数量的 Go 项目。在开发过程中,将GOPATH设置为您的一个项目。Go 编译器现在激活该项目。

通常的做法是在主目录下创建一个项目,并按如下方式设置GOPATH环境变量:

>mkdir /home/naren/myproject
export GOPATH=/home/naren/myproject

现在,我们安装如下外部软件包:

go get -u -v github.com/gorilla/mux

Go 将名为mux的项目复制到当前激活的项目myproject中。

对于 Go-get,使用-u标志安装外部包的更新依赖项,并使用-v查看详细的安装细节。

一个典型的围棋项目具有以下结构,如围棋官方网站所述:

在进一步挖掘之前,让我们先了解该结构:

  • bin:存储我们项目的二进制文件;可直接运行的可交付二进制文件
  • pkg:包含包对象;提供包方法的已编译程序
  • src:项目源代码、测试和用户包所在的位置

在 Go 中,您导入到主程序中的所有包都具有相同的结构,github.com/user/project。但是谁创建了所有这些目录呢?开发者应该这样做吗?不。开发人员有责任为其项目创建目录。表示他/她只创建目录src/github.com/user/hello

当开发人员运行以下命令时,如果目录 bin 和 package 以前不存在,则会创建它们。.bin由我们的项目源代码的二进制组成,.pkg由我们在 Go 程序中使用的所有内部和外部包组成:

 go install github.com/user/project

根据我们迄今为止构建的概念,让我们编写第一个基本的 REST 服务。此服务从客户端获取数字范围(1-10)并返回其罗马字符串。非常原始,但比 Hello World 更好。

设计:

我们的 RESTAPI 应该从客户端获取一个整数,并返回罗马等价物。

API 设计文档的块可能如下所示:

| HTTP 动词 | 路径 | 动作 | 资源 | | GET | /roman_number/2 | 显示 | roman_number |

执行情况:

现在我们将逐步实现前面的简单 API。

本项目的代码可在上找到 https://github.com/narenaryan/gorestful

如前所述,您应该首先设置GOPATH。假设GOPATH/home/naren/go。在下面的路径中创建一个名为romanserver的目录。将narenaryan替换为您的 GitHub 用户名(这只是属于不同用户的代码的名称空间):

mkdir -p $GOPATH/src/github.com/narenaryan/romanserver

我们的项目已经准备好了。我们还没有配置任何数据库。创建一个名为main.go的空文件:

touch $GOPATH/src/github.com/narenaryan/romanserver/main.go

API 服务器的主要逻辑进入这个文件。现在,我们可以创建一个数据文件,作为主程序的数据服务。再创建一个用于打包罗马数字数据的目录:

mkdir $GOPATH/src/github.com/narenaryan/romanNumerals

现在,在romanNumerals目录中创建一个名为data.go的空文件。到目前为止,src目录结构如下:

;

现在让我们开始向文件中添加代码。为罗马数字创建数据:

// data.go
package romanNumerals

var Numerals = map[int]string{
  10: "X",
  9: "IX",
  8: "VIII",
  7: "VII",
  6: "VI",
  5: "V",
  4: "IV",
  3: "III",
  2: "II",
  1: "I",
}

我们正在创建一个名为数字的地图。此映射包含将给定整数转换为罗马等效整数的信息。我们将把这个变量导入我们的主程序,以满足客户端的请求

打开main.go并添加以下代码:

// main.go
package main

import (
   "fmt"
   "github.com/narenaryan/romanNumerals"
   "html"
   "net/http"
   "strconv"
   "strings"
   "time"
)

func main() {
   // http package has methods for dealing with requests
   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
       urlPathElements := strings.Split(r.URL.Path, "/")
       // If request is GET with correct syntax
       if urlPathElements[1] == "roman_number" {
           number, _ := strconv.Atoi(strings.TrimSpace(urlPathElements[2]))
           if number == 0 || number > 10 {
           // If resource is not in the list, send Not Found status
               w.WriteHeader(http.StatusNotFound)
               w.Write([]byte("404 - Not Found"))
           } else {
             fmt.Fprintf(w, "%q", html.EscapeString(romanNumerals.Numerals[number]))
           }
       } else {
           // For all other requests, tell that Client sent a bad request
           w.WriteHeader(http.StatusBadRequest)
           w.Write([]byte("400 - Bad request"))
       }
   })
 // Create a server and run it on 8000 port
   s := &http.Server{
     Addr: ":8000",
     ReadTimeout: 10 * time.Second,
     WriteTimeout: 10 * time.Second,
     MaxHeaderBytes: 1 << 20,
   }
   s.ListenAndServe()
}

始终使用 Go fmt 工具格式化 Go 代码。

使用示例:go fmt github.com/narenaryan/romanserver

现在,使用 Go 命令install安装此项目:

go install github.com/narenaryan/romanserver

此步骤有两个功能:

  • 编译包romanNumerals并将副本放在$GOPATH/pkg目录中
  • $GOPATH/bin中放置二进制文件

我们可以按如下方式运行前面的 API 服务器:

$GOPATH/bin/romanserver

服务器已启动并在http://localhost:8000上运行。现在,我们可以使用类似于BrowserCURL命令的客户端向 API 发出GET请求。让我们使用适当的 APIGET请求触发一个CURL命令。

请求一如下:

curl -X GET "http://localhost:8000/roman_number/5" # Valid request

答复如下:

HTTP/1.1 200 OK
Date: Sun, 07 May 2017 11:24:32 GMT
Content-Length: 3
Content-Type: text/plain; charset=utf-8

"V"

让我们尝试一些格式不正确的请求。

请求二如下:

curl -X GET "http://localhost:8000/roman_number/12" # Resource out of range

答复如下:

HTTP/1.1 404 Not Found
Date: Sun, 07 May 2017 11:22:38 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8

404 - Not Found

请求三如下:

curl -X GET "http://localhost:8000/random_resource/3" # Invalid resource

答复如下:

"HTTP/1.1 400 Bad request
Date: Sun, 07 May 2017 11:22:38 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8
400 - Bad request

我们的小罗马数字 API 正在做正确的事情。正在返回正确的状态代码。这是所有 API 开发人员都应该牢记的一点。应告知客户出现问题的原因。

我们只是一次性更新了空文件,并开始运行服务器。现在让我解释文件main.go的每一部分:

  • 导入了几个包。github.com/narenaryan/romanNumerals是我们之前创建的数据服务。
  • net/http是我们用来通过HandleFunc功能处理 HTTP 请求的核心包。该函数的参数是http.Requesthttp.ResponseWriter。这两个函数处理 HTTP 请求的请求和响应。
  • r.URL.Path是 HTTP 请求的 URL 路径。对于卷曲请求一,它是/roman_number/5我们正在拆分此路径,并使用第二个参数作为资源,第三个参数作为值来获取罗马数字。Split函数位于名为strings的核心包中。
  • Atoi函数将字母数字字符串转换为整数。对于要使用的数字映射,我们需要将整数字符串转换为整数。Atoi函数来自一个名为strconv的核心包。
  • 我们使用http.StatusXXX设置响应头的状态码。响应对象上提供了WriteHeaderWrite函数,分别用于写入标题和正文。
  • 接下来,我们使用&http创建了一个 HTTP 服务器,同时初始化了一些参数,如地址、端口、超时等。
  • time包用于定义程序中的秒数。它表示,在 10 秒不活动之后,自动将 408 请求超时返回给客户端。
  • EscapeString将特殊字符转义为有效的 HTML 字符。例如,弗兰&弗雷迪的成为Fran &amp; Freddie's&#34
  • 最后,使用ListenAndServe功能启动服务器。它会让您的 web 服务器一直运行,直到您杀死它。

应该为他们的 API 编写单元测试。在接下来的章节中,我们将看到如何端到端测试 API

Gulp 是创建工作流的好工具。工作流是一个循序渐进的过程。它只是一个简化应用程序的任务。您需要在计算机上安装 NPM 和节点。我们使用 Gulp 监视文件,然后更新二进制文件并重新启动 API 服务器。听起来很酷,对吧?

supervisor 是一个应用程序,在应用程序被终止时重新加载服务器。将为您的服务器分配一个进程 ID。要正确地重新启动应用程序,我们需要杀死现有实例并重新启动应用程序。我们可以用围棋写一个这样的程序。但是为了不重新发明轮子,我们正在使用一个叫做 supervisord 的流行程序。 

有时,由于操作系统重新启动或崩溃,web 应用程序可能会停止。每当您的 web 服务器被杀死时,主管的工作就是让它恢复活力。即使重新启动系统也无法使 web 服务器离开客户。因此,严格使用 supervisord 进行应用程序监控。

我们可以通过apt-get命令在 Ubuntu 16.04 上轻松安装 supervisord:

sudo apt-get install -y supervisor

这将安装两个工具,supervisorsupervisorctlsupervisorctl用于控制监控命令,添加任务、重启任务等。

在 macOS X 上,我们可以使用brew命令安装supervisor

brew install supervisor

现在,在以下位置创建一个配置文件:

/etc/supervisor/conf.d/goproject.conf

您可以添加任意数量的配置文件,supervisord 将其视为单独的进程来运行。将以下内容添加到前面的文件:

[supervisord]
logfile = /tmp/supervisord.log
[program:myserver]
command=$GOPATH/bin/romanserver
autostart=true
autorestart=true
redirect_stderr=true

默认情况下,我们在/etc/supervisor/有一个名为.supervisord.conf的文件。请看它以获取更多参考。在 macOS X 中,相同的文件将位于/usr/local/etc/supervisord.ini

转到前面的配置:

  • [supervisord]部分告诉 supervisord 日志文件的位置
  • [program:myserver]是遍历给定目录并执行给定命令的任务块

现在我们可以要求我们的supervisorctl重新读取配置并重新启动任务(流程)。对此,只需说:

  • supervisorctl reread
  • supervisorctl update

然后,用命令启动supervisorctl

supervisorctl

您将看到如下内容:

supervisorctl is a great tool for controlling supervisor programs.

由于我们在 supervisor 配置文件中将 romanserver 命名为myserver,因此我们可以从supervisorctl启动、停止和重新启动该程序。

在上一节中,我们对 Gulp 做了一点介绍,我们将编写一个 gulpfile,告诉计算机执行一些任务。

我使用npm安装气枪和气枪外壳

**```go npm install gulp gulp-shell


之后,在项目的根目录中创建一个`gulpfile.js`。这里是`github.com/src/narenaryan/romanserver`。现在将此内容添加到`gulpfile.js`。首先,每当文件发生更改时,都会执行 InstallBinary 任务。然后,将重新启动监控程序。监视任务查找任何文件更改并执行前面的任务。我们还对任务进行排序,以便它们一个接一个地同步发生。所有这些任务都是吞咽任务,可以通过`gulp.task`功能定义。它接受两个任务名为 task 的参数。`sell.task`允许 Gulp 执行系统命令:

```go
var gulp = require("gulp");
var shell = require('gulp-shell');

// This compiles new binary with source change
gulp.task("install-binary", shell.task([
 'go install github.com/narenaryan/romanserver'
]));

// Second argument tells install-binary is a deapendency for restart-supervisor
gulp.task("restart-supervisor", ["install-binary"], shell.task([
 'supervisorctl restart myserver'
]))

gulp.task('watch', function() {
 // Watch the source code for all changes
 gulp.watch("*", ['install-binary', 'restart-supervisor']);

});

gulp.task('default', ['watch']);

现在,如果您在source目录中运行gulp命令,它将开始监视您的源代码更改:

gulp

现在,如果我们修改代码,那么代码将被编译、安装,服务器将在闪存中重新启动:

在 gulpfile 中,我们执行以下说明:

  1. 进口啤酒和啤酒壳。
  2. 创建以shell.task为执行函数的任务。
  3. shell.task可以执行命令行指令。将 shell 命令保存在该函数中。
  4. 添加监视源文件的监视任务。修改文件时将执行任务列表。
  5. 创建用于运行的默认任务。给它加一块手表。

对于这类用例来说,Gulp 是一个很好的工具。因此,请在查阅 Gulp 的官方文件 http://gulpjs.com/

在本章中,我们介绍了 RESTAPI。我们看到 REST 不是一个协议,而是一个架构模式。HTTP 是我们可以实现 REST 服务的实际协议。我们深入了解了 RESTAPI 的基本原理,以明确它们实际上是什么。然后我们探讨了 web 服务的类型。在 REST 之前,我们有一种称为 SOAP 的东西,它使用 XML 作为数据格式。REST 使用 JSON 作为主要格式。REST 有动词和状态代码。我们看到了给定的状态代码所指的内容。我们构建了一个简单的服务,为给定的数字提供罗马数字。在这个过程中,我们还看到了如何打包 Go 项目。我们理解了 GOPATH 环境变量。它是一个在 Go 中定义变量的工作区。所有包和项目都位于该路径中。然后,我们看到了如何在 supervisord 和 Gulp 的帮助下动态地重新加载开发项目。这些是节点工具,但可以帮助我们保持 Go 项目的启动和运行。

在下一章中,我们将深入研究 URL 路由。从内置路由器开始,我们探索 Gorilla Mux,这是一个强大的 URL 路由库。**

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

技术领导力实战笔记 -〔TGO鲲鹏会〕

玩转webpack -〔程柳锋〕

罗剑锋的C++实战笔记 -〔罗剑锋〕

软件设计之美 -〔郑晔〕

手机摄影 -〔@随你们去〕

物联网开发实战 -〔郭朝斌〕

超级访谈:对话汤峥嵘 -〔汤峥嵘〕

快速上手C++数据结构与算法 -〔王健伟〕

结构思考力 · 透过结构看思考 -〔李忠秋〕