使用烧瓶,mongodb和docker创建API
#docker #flask #mongodb #postman

在本文中,我们将考虑使用Python软件包,Flask,使用MongoDB为我们的数据库创建API,创建Postman测试并使用Docker运行所有内容。

Architecture Diagram

您可以在我的example-api repo中找到所有我会通过的代码。

设置容器

首先,我们将使用Docker-Compose设置容器。在新的项目文件夹中,创建一个具有以下内容的docker-compose.yml文件:

version: '3.9'

services:

  db:
    image: mongo:5.0
    ports:
      - 27017:27017
    networks:
      - backend
    env_file: .env

  api:
    build: ./
    ports:
      - 5000:5000
    volumes:
      - ./src:/src
    networks:
      - frontend
      - backend
    env_file: .env

  postman:
    image: postman/newman:alpine
    command:
      run ${POSTMAN_COLLECTION} -k
      -r cli,json
      --reporter-json-export="reports/api_test_report.json"
    volumes:
      - ./tests:/etc/newman
    depends_on:
      - "api"
      - "db"
    networks:
      - backend

networks:
  frontend:
  backend:

我们有3个容器:

  • db-用于我们的数据库,运行mongodb image

  • api-用于我们的烧瓶API,由Dockerfile

  • 构建
  • postman-用于测试我们的API,执行Postman Collection测试

烧瓶API

对于烧瓶API,创建一个Dockerfile,并具有以下内容:

FROM python:3.8-slim-buster

WORKDIR /app

COPY src/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

使用requirements.txt文件在项目文件夹中创建一个新的src文件夹:

mkdir src
touch requirements.txt

对于requirements.txt的内容,添加以下内容:

flask
flask-pymongo

建造我们的容器时,将安装这些软件包。

接下来,我们需要创建应用程序,在src文件夹中创建一个app.py文件:

import os
import json
from flask import Flask, request
from flask_pymongo import PyMongo
from bson import json_util, ObjectId

MONGODB_URI = os.environ.get("MONGODB_ENDPOINT")

app = Flask(__name__)
app.config["MONGO_URI"] = MONGODB_URI
mongo = PyMongo(app)

def parse_json(data):
    return json.loads(json_util.dumps(data))

@app.route('/')
def hello_world():
    return 'Hello, World!'

@app.route('/items', methods=['GET'])
def get_all_items():
    items = list(mongo.db.items.find())
    return parse_json(items), 200

@app.route('/items', methods=['POST'])
def create_item():
    item = request.get_json()
    inserted_item = mongo.db.items.insert_one(item)
    return parse_json(inserted_item.inserted_id), 201

@app.route('/items/<item_id>', methods=['GET'])
def get_item(item_id):
    item = mongo.db.items.find_one_or_404({'_id': ObjectId(item_id)})
    return parse_json(item), 200

@app.route('/items/<item_id>', methods=['PUT'])
def update_item(item_id):
    item = request.get_json()
    item_id_obj = ObjectId(item_id)
    result = mongo.db.items.update_one({'_id': item_id_obj}, {'$set': item})
    if result.matched_count == 0:
        return parse_json({'error': 'Item not found'}), 404
    updated_item = mongo.db.items.find_one({'_id': item_id_obj})
    return parse_json({'message': 'Item updated successfully', 'item': updated_item}), 200

@app.route('/items/<item_id>', methods=['DELETE'])
def delete_item(item_id):
    item_id_obj = ObjectId(item_id)
    result = mongo.db.items.delete_one({'_id': item_id_obj})
    if result.deleted_count == 0:
        return parse_json({'error': 'Item not found'}), 404
    return parse_json({'message': 'Item deleted successfully'}), 200

if __name__ == "__main__":
    app.run(debug=True)

这是我们的整个应用。在脚本的顶部,我们正在导入所需的软件包,初始化烧瓶应用程序和mongodb实例。

我们有一个parse_json功能,用于格式化MongoDB的响应,hello_world函数和用于创建,阅读,更新和删除记录的CRUD操作。

您可以在脚本中看到,我们使用的是环境变量,os.environ.get("MONGODB_ENDPOINT")。要设置此问题,请在项目root目录中创建一个.env文件,并使用以下内容:

MONGODB_ENDPOINT=mongodb://db:27017/example
POSTMAN_COLLECTION=Example_API.postman_collection.json

提出命令

最后,在项目root目录中创建一个Makefile,并使用以下内容:

DOCKER_COMPOSE = docker-compose up -d 
POSTMAN_COLLECTION ?=

.PHONY: rebuild
rebuild:
    docker-compose up -d --build

.PHONY: start
start: _start_api _start_db

.PHONY: stop
stop:
    docker-compose down

.PHONY: tests
tests:
    @echo "Running tests"
    docker-compose up postman

_start_api:
    $(DOCKER_COMPOSE) api

_start_db:
    $(DOCKER_COMPOSE) db

此Makefile允许我们轻松启动并停止Docker容器。

我们的start命令仅启动apidb容器。仅当我们执行tests方法时,postman容器才能运行,该方法立即运行测试并终止容器。

运行API

到目前为止,您的项目目录应该看起来像这样:

.
├── Dockerfile
├── Makefile
├── docker-compose.yml
├── .env
└── src
    ├── app.py
    └── requirements.txt

我们现在可以运行API!

要构建并启动API,运行:

make start

http://localhost:5000。您应该看到一个“你好,世界!”消息:

Browser screenshot showing Hello World message

ðAPI启动并运行!

API具有以下端点:

端点 方法 描述
/ GET 你好世界
/items GET 获取所有项目
/items/{id} GET 获取项目
/items POST 创建项目
/items/{id} PUT 更新项目
/items/{id} DELETE 删除项目

如果要在API中添加测试,请继续阅读!

测试API

随着API的启动和运行,我们可以编写邮递员测试。

打开Postman并创建一个新系列。

单击集合名称,然后转到Variables选项卡。

创建一个名为base_url的新变量,具有初始值和当前值http://localhost:5000。单击“保存图标”。

Screenshot of Postman Collection variables

单击Add a request并创建一个名为“ Hello World”的请求。这将是一个请求。将请求URL设置为{{base_url}},这将使用我们的集合变量中的值。单击SaveSend

Postman将执行请求,您将在下面看到API的响应:

Screenshot of Postman request

打开Tests选项卡。在右边,有一个SNIPPETS工具栏。在列表中,单击Status code: Code is 200。这将添加这样的测试:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

单击SaveSend。该测试将在“ Test Results”选项卡中运行并显示在输出面板中:

Screenshot showing Postman test results

现在,我们已经为Hello World端点设置了测试。现在我们需要为其他端点创建测试。

重复此过程,为每个端点创建新请求并测试:

获取所有项目

请求名称:获取所有项目

请求方法:获取

请求URL: {{base_url}}/items

测试:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

创建项目

请求名称:创建项目

请求方法:发布

请求URL: {{base_url}}/items

测试:

pm.test("Status code is 201", function () {
    pm.response.to.have.status(201);
});
pm.test("Returned item id", function () {
    jsonData = pm.response.json()
    pm.collectionVariables.set("item_id", jsonData.$oid);
});

对于此测试,项目创建后的成功响应状态代码为201。我们还进行了第二次测试,以获取我们创建的项目的返回的item_id并将其设置为集合变量。然后,我们可以在未来测试中使用该值。

身体:

由于这是POST请求,我们将通过内容发送以创建项目。在Body选项卡中,单击raw并粘贴以下内容:

{
    "title": "Hello, World!"
}

执行请求时,此内容将发送到我们的API。

获取项目

请求名称:获取项目

请求方法:获取

请求URL: {{base_url}}/items/{{item_id}}

测试:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
pm.test("Returned result matches test item", function () {
    jsonData = pm.response.json()
    item_id = pm.collectionVariables.get("item_id");
    pm.expect(jsonData._id.$oid).to.eql(item_id);
    pm.expect(jsonData.title).to.eql("Hello, World!");
});

此请求使用收集值中保存的item_id。在测试中,我们正在检查响应成功(200),并验证返回的item_id并与我们在Create Item测试中创建的内容匹配。

更新项目

请求名称:更新项目

请求方法: put

请求URL: {{base_url}}/items/{{item_id}}

测试:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
pm.test("Result contains updated item", function () {
    jsonData = pm.response.json()
    item_id = pm.collectionVariables.get("item_id");
    pm.expect(jsonData.item._id.$oid).to.eql(item_id);
    pm.expect(jsonData.item.title).to.eql("Hello, there!");
});

再次,此请求使用收集值中保存的item_id。这次,我们正在检查返回的item_id与我们修改的项目匹配,而title与我们的请求Body中的更新值匹配。

身体:

{
    "title": "Hello, there!"
}

删除项目

请求名称:删除项目

请求方法:删除

请求URL: {{base_url}}/items/{{item_id}}

测试:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

执行所有测试

随着所有请求和测试添加到我们的集合中,我们可以一起运行它们。

单击集合名称旁边的三个点,然后单击Run collection。单击Run Example API执行所有测试。

Postman将通过您的API上的测试进行摘要:

Screenshot of Postman collection test summary

â如果一切都正确设置,所有测试都应成功!

自动化测试

可以通过单击“集合名称”和“ Export”旁边的三个点来导出包含请求和测试的邮政集合。将收藏集导出到您的项目文件夹中,并将其引入一个名为tests的新文件夹。命名集合Example API.postman_collection.json

在我们项目中的收藏中,我们可以运行make tests。这将运行postman容器并执行测试。您可能会注意到所有测试都失败了。这是因为base_url变量设置为http://localhost:5000,并且在从postman容器中运行时,烧瓶API是无法实现的。

要解决此问题,请将--env-var "base_url=${API_BASE_URL}"添加到docker-compose.yml中定义的邮政容器中:

  postman:
    image: postman/newman:alpine
    command:
      run ${POSTMAN_COLLECTION} -k 
      --env-var "base_url=${API_BASE_URL}"
      -r cli,json
      --reporter-json-export="reports/api_test_report.json"
    volumes:
      - ./tests:/etc/newman
    depends_on:
      - "api"
      - "db"
    networks:
      - backend

并将API_BASE_URL=http://api:5000添加到.env文件:

MONGODB_ENDPOINT=mongodb://db:27017/example
POSTMAN_COLLECTION=Example_API.postman_collection.json
API_BASE_URL=http://api:5000

api是指docker-compose.yml中定义的api docker容器。通过使用此功能,postman容器将能够到达烧瓶API。现在重新运行make tests,他们都应该成功。

github动作

在项目目录中,创建一个.github文件夹,其中的workflows文件夹和一个test.yml文件:

mkdir .github
mkdir .github/workflows
touch .github/workflows/test.yml

.github/workflows/test.yml文件中,添加以下内容:

name: Test API
on:
  push:
    branches:
      - main
  pull_request:
    branches:    
      - main

jobs:
  test-api:
    environment:
      name: tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run tests
      run: make tests

提交并将您的更改推向github。当推动更改或创建main的拉普尔时,github操作将运行make tests

Screenshot of postman tests running in Github Actions

ð您现在有一个带有自动测试的示例API!

如果您陷入了任何步骤,或者只是想要完整的解决方案,请参考或克隆我的example-api repo。