使用Swagger加速Node.js中的RESTful API开发

介绍 ( Introduction )

So it happened I started this project with friend of mine, a new app to launch on the mobile market. I offered to write the server-side, a simple web API. Once he got my code he immediately asked me "Sam how do I know the routes and so on?! Write a documentation or something!". And then... Swagger came up.

因此,碰巧我与我的一位朋友(一个即将在移动市场上推出的新应用)开始了这个项目。 我提供编写服务器端的简单Web API。 一旦得到我的代码,他立即问我“山姆,我怎么知道路线等等?!写一个文档或其他东西!”。 然后……昂首阔步。

I presume many of you encountered this situation before or will in the future, a developer may write the best web API service but without proper documentation how can other developers discover and use it? So in this tutorial I want to introduce Swagger, a famous open-source framework to help you write RESTful APIs.

我想你们中的许多人以前或将来会遇到这种情况,开发人员可能会编写最佳的Web API服务,但是如果没有适当的文档,其他开发人员如何才能发现和使用它? 因此,在本教程中,我想介绍Swagger ,这是一个著名的开源框架,可以帮助您编写RESTful API。

Swagger offers:

Swagger提供:

  • Interactive documentation.

    交互式文档。
  • Discoverability.

    可发现性。
  • Client SDK generation.

    客户端SDK生成。

In addition, it is supported by many programming languages.

另外,许多编程语言都支持它。

One feature I like is the mock mode, which allows developers to design routes without writing a single line of code in javascript.

我喜欢的一个功能是模拟模式,该模式允许开发人员设计路线而无需在javascript中编写任何代码。

This is the approach I am going to follow in this tutorial: We first focus on the design and then on the code.

这是本教程中我将遵循的方法:我们首先关注设计,然后关注代码。

In the tutorial we are going create a RESTful API to manage our movies collection.

在本教程中,我们将创建一个RESTful API来管理电影收藏。

You can directly clone the app repository from my Github here.

您可以在此处从我的Github直接克隆应用程序存储库。

先决条件 (Prerequisites)

  • A basic understanding of node.js, how to install packages etc.

    基本了解node.js,如何安装软件包等。
  • CRUD cycle.

    CRUD周期。
  • Yaml syntax.

    Yaml语法

Let's move to the installation of Swagger.

让我们转到Swagger的安装。

安装并运行示例 ( Installation and run the example )

As said before Swagger is supported in node.js through its own module, here is the link to the github project: The documentation is pretty straight-forward to help beginners understand and configure Swagger.

正如在通过自己的模块在node.js中支持Swagger之前所说的那样, 是github项目的链接:该文档非常简单明了,可以帮助初学者了解和配置Swagger。

Once we open the command line we install the module (global).

打开命令行后,我们将安装模块(全局)。

npm install -g swagger

Now let's create our project.

现在让我们创建我们的项目。

swagger project create movie-collection

The prompt asks for the framework we want to use, choose express. Once completed, let's take a look at the project directory:

提示询问我们要使用的框架,选择express 。 完成后,让我们看一下项目目录:

-- api  
---- controllers 
------ hello_world.js
---- helpers
---- mocks
---- swagger
------ swagger.yaml

-- config 
---- default.yaml

--test
---- api
------ controllers
-------- hello_world.js
------ helpers

-- app.js
-- package.json

N.B. Each subfolder contains a README.md that for simplicity I didn't include above.

注意:每个子文件夹都包含一个README.md ,为简单起见,我没有在上面包含它。

  • api: As the name suggests, everything related to the API development, the controllers, mocks, helpers. A special mention goes to the /swagger folder which contains the file swagger.yaml, an important file we are going to edit to define everything related to the project information and routes. Throughout the tutorial I am going to explain it all so don't worry for now.

    api :顾名思义,所有与API开发有关的内容,控制器,模拟程序,帮助程序。 特别值得一提的是/swagger文件夹,其中包含文件swagger.yaml ,这是我们将要编辑的重要文件,用于定义与项目信息和路线相关的所有内容。 在整个教程中,我将对所有内容进行解释,所以现在不必担心。
  • config: It contains default.yaml that, as the documentation states, drives the application's config directory. Though we are not going to customize the file I still suggest you guys to read the related documentation to understand the engine beneath the "magic" of declaring APIs without writing a line of code but through yaml or json.

    config :它包含default.yaml ,如文档所述,该文件用于驱动应用程序的config目录。 尽管我们不会自定义文件,但我仍然建议大家阅读相关文档,以了解通过声明api而不是通过yaml或json编写API的“魔术”引擎。
  • test: Your test for controllers and helpers are (guess what!) created here.

    测试 :您在此处创建了对控制器和辅助程序的测试(猜测是什么!)。

Obviously, app.js is the main file which runs the server.

显然, app.js是运行服务器的主要文件。

Have you noticed that there is already a controller called hello_world.js? Whenever you create a new project, the module adds an example route, a GET request to /hello which takes a name as parameter and greets the person. Not original at all, I give you that, but as we are beginners it is good see Swagger in action and get insight on how it works.

您是否注意到已经有一个名为hello_world.js的控制器? 每当您创建新项目时,模块都会向/hello添加示例路线,一个GET请求,该请求以名称作为参数并问候此人。 完全不是原创,我为您提供,但是作为我们的初学者,很高兴看到Swagger发挥作用并深入了解其工作原理。

What about taking a look at the example in action?

看看实际的例子呢?

In order to run the example I have to introduce Swagger editor.

为了运行该示例,我必须介绍Swagger 编辑器

Swagger editor is an elegant browser-based editor which really simplifies our efforts to develop a web API. In particular, it provides:

Swagger编辑器是一款优雅的基于浏览器的编辑器,它确实简化了我们开发Web API的工作。 特别是,它提供:

  • Validation and endpoint routing.

    验证和端点路由。
  • Docs on the fly generation.

    即时生成文档。
  • Easy-to-read Yaml.

    易读的Yaml。

The picture above shows you the UI of the Swagger editor of our app. On the left you can see the yaml file we are going to edit (swagger.yaml remember?) while on the right the list of routes. By clicking on any of them we can have a good understanding of the parameter required, the format of request and responses, more generally a description of the route.

上图显示了我们应用程序的Swagger编辑器的UI。 在左侧,您可以看到我们要编辑的yaml文件( swagger.yaml还记得吗?),而在右侧则是路由列表。 通过单击它们中的任何一个,我们都可以很好地了解所需的参数,请求和响应的格式,更笼统地说是路径的描述。

Now, to launch the example, start the app by running

现在,要启动示例,请运行以下命令启动应用程序

swagger project start

NB whenever we modify a file, it automatically restarts the server (great!)

注意,只要我们修改文件,它就会自动重新启动服务器(太好了!)

Then, open a second command line and launch the editor with:

然后,打开第二个命令行并使用以下命令启动编辑器:

swagger project edit

If everything went well the editor should open a new tab in your browser. On the right side of the page you should notice the example path for a GET request to /hello, so open the tab and, at the bottom, click on the button try this operation.

如果一切顺利,编辑器应在浏览器中打开一个新选项卡。 在页面的右侧,您应该注意到对/hello的GET请求的示例路径,因此打开选项卡,然后在底部单击按钮, 尝试执行此操作

We are asked to enter a paramenter name: Go for it and test the route.
Here is my result:

要求我们输入一个参数名称 :继续并测试路线。
这是我的结果:

Let's look at the yaml file on the left, precisely on the path part:

让我们在路径部分的左侧看yaml文件:

paths:
  /hello:
    # binds a127 app logic to a route
    x-swagger-router-controller: hello_world
    get:
      description: Returns 'Hello' to the caller
      # used as the method name of the controller
      operationId: hello
      parameters:
        - name: name
          in: query
          description: The name of the person to whom to say hello
          required: false
          type: string
      responses:
        "200":
          description: Success
          schema:
            # a pointer to a definition
            $ref: "#/definitions/HelloWorldResponse"
        # responses may fall through to errors
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

This short piece of yaml is what we need to define our routes:

这短短的yaml是我们定义路线所需要的:

x-swagger-router-controller: This is the controller, the file we have in the /api/controllers/hello_world.js.

x-swagger-router-controller :这是控制器,即我们在/api/controllers/hello_world.js拥有的文件。

Then the http methods must be listed, in the example just a GET.

然后必须列出http方法,在示例中只是GET。

operationId: This refers to the function, in the controller, in charge of the business logic.

operationId :这是指控制器中负责业务逻辑的功能。

parameters: The list of required parameters are defined here. The parameter name is the only one and you may see that it is in the path, it is not required and it is a string.

parameters :此处定义了所需参数的列表。 参数name是唯一的,您可能会看到它路径中, 不是必需的,并且是字符串

When it comes to the responses, Swagger shows its potential:

谈到回应时,Swagger展示了其潜力:

We can define a different response for each situation, given the http status or errors, a particular response can be defined. In the example, for the status "200" there is a pointer $ref to a response definition while for other statuses, we go to default with its own $ref.
Why is that? In order to keep the yaml file clean, we can define all our response under definitions while at the same time reusing the definition for different responses.

我们可以为每种情况定义不同的响应,给定http状态或错误,可以定义一个特定的响应。 在该示例中,对于状态“ 200”,存在指向响应定义的指针$ref ,而对于其他状态,我们将使用其自己的$ref defaultdefault
这是为什么? 为了保持yaml文件的干净,我们可以在definitionsdefinitions所有响应,同时将定义重新用于不同的响应。

Before we finish with the yaml file, let's take a look at one more piece of code:

在完成yaml文件之前,让我们看一下另一段代码:

consumes:
  - application/json
# format of the responses to the client (Accepts)
produces:
  - application/json

On the top of the file it has defined what the routes consume and produce, it is the format of the request parameters and related response. Those rules are currently applying to all the paths defined in the file. It is also possible to customize these rules for a single path/http method by including these properties inside of it.
However, for the purpose of the tutorial, we will stick to application/json for all the routes, so a single definition on top is more than enough.

在文件的顶部,它定义了路由消耗产生的内容 ,这是请求参数和相关响应的格式。 这些规则当前适用于文件中定义的所有路径。 通过在其中包含这些属性,也可以为单个path / http方法自定义这些规则。
但是,出于本教程的目的,我们将对所有路由都使用application / json ,因此在顶部的单个定义就足够了。

Lastly, let's take a look inside /api/controllers/hello_world.js to check the function hello:

最后,让我们看一下/api/controllers/hello_world.js来检查功能hello

function hello(req, res) {
    var name = req.swagger.params.name.value || 'stranger';
    var hello = util.format('Hello, %s', name);
    res.json(hello);
}

Nothing exotic here, though you should notice that with Swagger the req object has a new property swagger which contains the parameters we defined.

尽管您应该注意到,使用Swagger, req对象具有一个新属性swagger ,其中包含我们定义的参数,但这里并没有什么奇特的地方。

In the next section we finally start developing our app.

在下一节中,我们终于开始开发我们的应用程序。

使用模拟 ( Working with mocks )

Working with mocks does not require any code to be written, just editing the yaml file, so we can focus on the design part first.

使用模拟程序不需要编写任何代码,只需编辑yaml文件,因此我们可以首先关注设计部分。

First of all, rerun the project adding the flag -m to the command which tells Swagger to run in mock mode, then run the editor in the second window.

首先,重新运行项目,将标志-m添加到命令Swagger以模拟模式运行的命令中,然后在第二个窗口中运行编辑器。

swagger movie-collection start -m

GET /电影 (GET /movie)

The first route returns the complete list of movies in our collection.

第一条路线返回我们收藏的电影的完整列表。

Delete the example /hello and add these lines of code:

删除示例/ hello并添加以下代码行:

/movie:
    # our controller name
    x-swagger-router-controller: movie
    get:
      description: get the movies list
      # define the type of response for Success "200" and Error
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GetMoviesListResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

First, we define our controller movie but there is no need to define the operationId in charge of the business logic. In addition, no need for the parameters. Take note, the successful response has a schema GetMoviesResponse.
The definitions, as usual, are listed at the bottom of the file, so copy and paste this code:

首先,我们定义了控制器movie但是不需要定义负责业务逻辑的operationId 。 另外,不需要参数。 注意,成功的响应具有架构GetMoviesResponse
像往常一样,这些定义在文件底部列出,因此请复制并粘贴以下代码:

GetMoviesListResponse:
    required:
      - movies
    properties:
      # The array of movies
      movies:
        type: array
        items: 
          type: object
          properties:
            id:
              type: string
            title:
              type: string
            year:
              type: number

The response is nothing but an array of movies which contain a unique id, a title and the year.

响应不过是一系列包含唯一ID,标题和年份的电影。

NB swagger editor autosaves whatever we input, which is great for us, but I noticed that sometimes is gets stuck. So if that is the case for you, try to refresh the page.

NB swagger编辑器会自动保存我们输入的所有内容,这对我们来说非常好,但是我注意到有时会卡住。 因此,如果您是这种情况,请尝试刷新页面。

Here is the code inside the editor which automatically created a better view on the right.

这是编辑器中的代码,该代码会自动在右侧创建一个更好的视图。

Now test the route and check the result:

现在测试路线并检查结果:

Since we didn't define any operationId, in mock mode swagger returns a standard value according to the properties type.
Since id and title are strings, it returns "string" while for the year, as a number, it returns 1.

由于我们未定义任何operationId,因此在模拟模式下swagger将根据属性类型返回标准值。
由于idtitle是字符串,因此返回“ string”,而对于年份,则返回1。

For the record, if not satisfied by the standard values, it is always possible to define the mock controller in /api/mocks and add a operationId. Then, the business logic just returns the answer in the format you defined in the definitions, but you can customize the values for each property.
However, I am not going to cover this step because I want to stick to the promise of working with mocks without writing a single line of code.

作为记录,如果标准值不满意,总是可以在/api/mocks定义模拟控制器并添加operationId 。 然后,业务逻辑仅以您在定义中定义的格式返回答案,但是您可以自定义每个属性的值。
但是,我将不涉及这一步骤,因为我希望遵守无需编写任何代码即可使用模拟的承诺。

开机自检/电影 (POST /movie)

Let's create a route to add a new movie to the list. After get: add the following yaml lines:

让我们创建一条将新电影添加到列表的路线。 get:添加以下yaml行:

post:
      description: add a new movie to the list
      # movie info to be stored
      parameters:
        - name: title
          description: Movie properties
          in: body
          required: true
          schema:
            $ref: "#/definitions/Movie"
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

This time we have a parameter in the body which is simply the movie object with a title and year. Let's add the schema in the definitions:

这次,我们在主体中有一个参数,该参数只是带有标题和年份的电影对象。 让我们在定义中添加模式:

Movie:
    type: object
    properties:
      title:
        type: string
        description: task object name
      year:
        type: number
        description: task description
    required:
      - title
      - year

NB We declared the two fields title and year as required.

注意:我们根据需要声明了两个字段的titleyear

What about the responses? The error response is always the same (reusability) while we defined a GeneralResponse. So, copy this code in the definition:

那回应呢? 当我们定义GeneralResponse时,错误响应始终是相同的(可重用性)。 因此,将以下代码复制到definition

GeneralResponse:
    type: object
    properties:
      success:
        type: number
        description: returns 1 if successful
      description:
        type: string
        description: a short comment 
    required:
      - success
      - description

So to speak, we have a success field set to 1 and a description of the operations. This will be a common response for all our routes when we create or update a movie, the only difference will be the description text.

可以这么说,我们将成功字段设置为1,并描述了操作。 当我们创建或更新电影时,这将是我们所有路线的普遍回答,唯一的不同就是描述文字。

Let's try to add a movie:

让我们尝试添加电影:

Here is the result:

结果如下:

GET /电影/ {id} (GET /movie/{id})

Now let's retrieve a single movie. This time we need the parameter in the path, precisely the unique id.

现在,让我们检索一部电影。 这次我们需要路径中的参数,即唯一ID。

We first need to create a new path called /movie/{id} so copy and paste the following inside of it:

我们首先需要创建一个名为/movie/{id}的新路径,因此将以下内容复制并粘贴到其中:

/movie/{id}:
    # our controller name
    x-swagger-router-controller: movie
    get:
      description: get a movie
      # define the type of response for Success "200" and Error
      parameters:
        - name: id
          type: string
          in: path
          required: true
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GetMovieResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

Notice that this time the parameter, in its own field in has path as value.

请注意,这次参数在其自己的字段in具有path作为值。

The successful response it's the movie object itself, so we need to add GetMovieResponse to the list of definitions:

成功的响应是电影对象本身,因此我们需要将GetMovieResponse添加到定义列表中:

GetMovieResponse:
    required:
      - id
      - title
      - year
    properties:
      id:
        type: string
      title: 
        type: string
      year:
        type: number
  Movie:
    type: object
    properties:
      title:
        type: string
        description: task object name
      year:
        type: number
        description: task description
    required:
      - title
      - year

Let's try it, for the id parameter, we can mock it with a fake one:

让我们尝试一下,对于id参数,我们可以使用伪造的模拟它:

and here is the expected response:

这是预期的响应:

PUT /电影/ {id} (PUT /movie/{id})

Now it's time create the route to update a movie, given the id in the path and a new title and year in the body. Copy the following code in the editor:

现在,根据路径中的ID和正文中的新标题和年份,现在就可以创建更新电影的路线。 在编辑器中复制以下代码:

put:
      description: update a movie
      # define the parameters
      parameters:
        - name: id
          description: Movie id
          type: string
          in: path
          required: true
        - name: title
          description: Movie properties
          in: body
          required: true
          schema:
            $ref: "#/definitions/Movie"
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

I really like the order to define the parameters, no matter if they come from the body, path, header etc. They are all consequently listed where in assumes the correct value.

我真的很喜欢定义参数的顺序,无论它们是否来自正文,路径,标题等。因此,所有列出的参数in假定in为正确的值。

Notice the GeneralResponse, the same schema we used for adding a new movie

注意GeneralResponse,与我们用于添加新电影的模式相同

So, update a file:

因此,更新文件:

And the result:

结果:

删除/ movie / {id} (DELETE /movie/{id})

The last operation, let's delete a movie by a given id.

最后一个操作,让我们删除具有给定ID的电影。

Copy and past the following code:

复制并粘贴以下代码:

delete:
      description: delete a movie
      # define the parameters
      parameters:
        - name: id
          description: Movie id
          type: string
          in: path
          required: true
      responses:
        "200":
          description: Success
          schema:
            $ref: "#/definitions/GeneralResponse"
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"

There is no need to further discuss the yaml file, we simply define the parameter and the response is GeneralResponse.

无需进一步讨论yaml文件,我们只需定义参数,响应为GeneralResponse。

Try the operation:

尝试操作:

Here is the result:

结果如下:

...And congratulations! We finished the design and now it's about time to write some javascript code.

...恭喜! 我们完成了设计,现在是时候编写一些javascript代码了。

实施控制器并运行真实应用 ( Implement the controller and run the real app )

The first thing to implement is the data-storage logic. Well, for the purpose of the tutorial we are not going to use any real database (MySQL, Mongo...) but simply store the data in memory for the time the app runs.

首先要实现的是数据存储逻辑。 好吧,对于本教程而言,我们不会使用任何真实的数据库(MySQL,Mongo ...),而只是在应用程序运行时将数据存储在内存中。

We need to add a new package, crypto, to create the unique id for any stored movie:

我们需要添加一个新程序包crypto ,以为任何存储的电影创建唯一的ID:

npm install crypto --save

Let's create a file db.js /config. We are going to export a simple object which mocks the DB operations of add, retrieve, update and delete movies.

让我们创建一个文件db.js /config 。 我们将导出一个简单的对象,该对象模拟添加,检索,更新和删除影片的数据库操作。

Here is the code to copy

这是要复制的代码

'use strict;'
//Include crypto to generate the movie id
var crypto = require('crypto');

module.exports = function() {
    return {
        movieList : [],
        /*
         * Save the movie inside the "db".
         */
        save(movie) {
            movie.id = crypto.randomBytes(20).toString('hex'); // fast enough for our purpose
            this.movieList.push(movie);
            return 1;            
        },
        /*
         * Retrieve a movie with a given id or return all the movies if the id is undefined.
         */
        find(id) {
            if(id) {
                return this.movieList.find(element => {
                        return element.id === id;
                    });    
            }else {
                return this.movieList;
            }
        },
        /*
         * Delete a movie with the given id.
         */
        remove(id) {
            var found = 0;
            this.movieList = this.movieList.filter(element => {
                    if(element.id === id) {
                        found = 1;
                    }else {
                        return element.id !== id;
                    }
                });
            return found;            
        },
        /*
         * Update a movie with the given id
         */
        update(id, movie) {
            var movieIndex = this.movieList.findIndex(element => {
                return element.id === id;
            });
            if(movieIndex !== -1) {
                this.movieList[movieIndex].title = movie.title;
                this.movieList[movieIndex].year = movie.year;
                return 1;
            }else {
                return 0;
            }
        }        
    }
};  

The names of the functions are the same as the traditional operations with Mongoose. The first property is a list to store all the movies in the collection.

函数的名称与Mongoose的传统操作相同。 第一个属性是一个列表,用于存储收藏集中的所有电影。

  • Save: We need to create a unique id, here is where crypto comes to help.

    保存 :我们需要创建一个唯一的ID,这是加密技术可以提供帮助的地方。
  • Find: If the id is passed, then it returns the specific movie, all the list otherwise.

    查找 :如果传递了ID,则它将返回特定的电影,否则返回所有列表。
  • Remove: The .filter method of javascript arrays does pretty much what we need, remove and return the found movie.

    删除 :javascript数组的.filter方法几乎完成了我们所需的工作,删除并返回找到的电影。
  • Update: Update the movie if it exists in the collection.

    更新 :更新电影(如果该电影存在于收藏集中)。

NB it is very likely some of you may find better ways to implement the operations above, or even point out that I should return a shallow copy of the list instead of the property itself. Because this is just a simple demo to fake CRUD operations, I would suggest you customize my object for your needs or even connect to a real database.

注意,很可能有些人可能会找到更好的方法来实现上述操作,甚至指出我应该返回列表的浅表副本,而不是属性本身。 因为这只是伪造CRUD操作的简单演示,所以我建议您根据需要自定义对象,甚至连接到真实数据库。

电影控制器 (Movie controller)

In /api/controllers create movie.js and paste the following code:

/api/controllers创建movie.js并粘贴以下代码:

'use strict';
    // Include our "db"
    var db = require('../../config/db')();
    // Exports all the functions to perform on the db
    module.exports = {getAll, save, getOne, update, delMovie};

    //GET /movie operationId
    function getAll(req, res, next) {
      res.json({ movies: db.find()});
    }
    //POST /movie operationId
    function save(req, res, next) {
        res.json({success: db.save(req.body), description: "Movie added to the list!"});
    }
    //GET /movie/{id} operationId
    function getOne(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        var movie = db.find(id);
        if(movie) {
            res.json(movie);
        }else {
            res.status(204).send();
        }        
    }
    //PUT /movie/{id} operationId
    function update(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        var movie = req.body;
        if(db.update(id, movie)){
            res.json({success: 1, description: "Movie updated!"});
        }else{
            res.status(204).send();
        }

    }
    //DELETE /movie/{id} operationId
    function delMovie(req, res, next) {
        var id = req.swagger.params.id.value; //req.swagger contains the path parameters
        if(db.remove(id)){
            res.json({success: 1, description: "Movie deleted!"});
        }else{
            res.status(204).send();
        }

    }

We first included our db and then exported all the functions we need to use as operationId and handled the business logic.

我们首先包含了数据库,然后导出了需要用作operationId所有函数并处理了业务逻辑。

Notice that in the case that the id belongs to a movie, getOne, update, and delMovie return the same formatted json. We defined the same schema in the Swagger editor earlier.
On the other hand, in case there is no movie with the id of the request, we send a status of no content "204".

请注意,在id属于电影的情况下,getOne,update和delMovie返回相同格式的json。 我们之前在Swagger编辑器中定义了相同的架构。
另一方面,在没有带有请求ID的电影的情况下,我们发送的状态为“ 204”。

Now rerun the project without the -m flag and in the editor we have to add the operationId to each route, right after the "verb":

现在,重新运行不带-m标志的项目,并且在编辑器中,我们必须在“动词”之后的每个路径中添加operationId:

#in /movie
    get:
      operationId: getAll
#in /movie
    post:
      operationId: save
#in /movie/{id}
    get:
      operationId: getOne
#in /movie/{id}
    put:
      operationId: update
#in /movie/{id}
    get:
      operationId: delMovie

添加电影 (Add a movie)

The same as before, add a movie

和以前一样,添加电影

Result:

结果:

获取所有电影 (Get all the movies)

After we added more movies, let's try retrieve them all by a GET request to /movie:

添加更多电影后,让我们尝试通过对/ movie的GET请求来全部检索它们:

Side note, a true Star Wars fan must have noticed that year of "The Empire Strikes Back" is wrong, it was released in 1980. In addition I forgot a "s" in the title.

旁注,真正的《星球大战》迷一定已经注意到那年的《帝国反击战》是错误的,它于1980年发行。此外,我忘记了标题中的“ s”。

Good, time to update the movie!

好,是时候更新电影了!

更新电影 (Update a movie)

Add the id as a parameter in the PUT request to /movie/{id}

在PUT请求中将id作为参数添加到/ movie / {id}

Result:

结果:

Then, let's doublecheck the movie is really updated

然后,我们仔细检查一下电影是否确实已更新

观看一部电影 (Get a single movie)

We need a GET request to /movie/{id} with the movie id:

我们需要对带有电影ID的/ movie / {id}的GET请求:

Result:

结果:

It works!

有用!

删除电影 (Delete a movie)

Let's first get all the movies with a GET request to /movie:

首先让我们向/ movie发送GET请求来获取所有电影:

We decide to delete "The force awakens", so let's send a DELETE request to /movie/{id} with the right id:

我们决定删除“力量唤醒”,因此让我们向具有正确ID的/ movie / {id}发送DELETE请求:

Result:

结果:

Finally, let's double check whether it was really deleted or not by sending a GET request to /movie/{id}:

最后,让我们通过向/ movie / {id}发送GET请求来仔细检查它是否确实被删除:

The NOCONTENT response confirms it all worked.

NOCONTENT响应确认一切正常。

结论 ( Conclusions )

In this tutorial we have seen how Swagger helps to develop a robust RESTful API while at the same time providing an elegant documentation that can boost up cooperation among developers (for example backend and frontend developers).

在本教程中,我们了解了Swagger如何帮助开发健壮的RESTful API,同时提供了精美的文档,可以促进开发人员(例如后端和前端开发人员)之间的合作。

We covered all the basic requests, GET, POST, DELETE, UPDATE to manage a movie collection. At the same time we practiced with parameters by handling them from the body or the path.
The same can be done to handle parameters from the header of course, so I have a challenge for you:

我们介绍了用于管理电影收藏的所有基本请求,即GET,POST,DELETE,UPDATE。 同时,我们通过从身体或路径处理参数来练习参数。
当然,从标头处理参数也可以做到这一点,所以我对您来说是一个挑战:

On Scotch.io there is a cool tutorial to authenticate a node.js API with JSON web tokens. Why don't you try to rewrite with swagger to practice with header parameters?

在Scotch.io上,有一个很酷的教程,可以使用JSON Web令牌对node.js API进行身份验证。 您为什么不尝试用草率的重写来练习标头参数?

Last but not the least, I remind you again to take a look a the documentation on the official website for a deeper understanding of Swagger.

最后但并非最不重要的一点,我再次提醒您看一下官方网站上的文档 ,以更深入地了解Swagger。

翻译自: https://scotch.io/tutorials/speed-up-your-restful-api-development-in-node-js-with-swagger

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值