1

总之,我一直在按照烧瓶 restx 教程制作 api,但是我的端点都没有出现在 swagger 页面上(“规范中没有定义操作!”),每当我调用它们时,我都会得到 404

我主要按照这个https://flask-restx.readthedocs.io/en/latest/scaling.html创建了我的 api

我正在使用 python 3.8.3 作为参考。

我正在做的一个简化示例如下。

简而言之,我的问题是,我错过了什么?目前对为什么这不起作用空白。

目录结构

project/
  - __init__.py
  - views/
    - __init__.py
    - test.py
manage.py
requirements.txt

文件内容

要求.txt

Flask-RESTX==0.2.0
Flask-Script==2.0.6

管理.py

from flask_script import Manager

from project import app


manager = Manager(app)


if __name__ == '__main__':
    manager.run()

项目/初始化.py

from flask import Flask

from project.views import api


app = Flask(__name__)

api.init_app(app)

项目/视图/初始化.py

from flask_restx import Api, Namespace, fields


api = Api(
    title='TEST API',
    version='1.0',
    description='Testing Flask-RestX API.'
)

# Namespaces
ns_test = Namespace('test', description='a test namespace')

# Models
custom_greeting_model = ns_test.model('Custom', {
    'greeting': fields.String(required=True),
})

# Add namespaces
api.add_namespace(ns_test)

项目/视图/test.py

from flask_restx import Resource

from project.views import ns_test, custom_greeting_model


custom_greetings = list()


@ns_test.route('/')
class Hello(Resource):

    @ns_test.doc('say_hello')
    def get(self):
        return 'hello', 200


@ns_test.route('/custom')
class Custom(Resource):

    @ns_test.doc('custom_hello')
    @ns_test.expect(custom_greeting_model)
    @ns_test.marshal_with(custom_greeting_model)
    def post(self, **kwargs):
        custom_greetings.append(greeting)
        pos = len(custom_greetings) - 1

        return [{'id': pos, 'greeting': greeting}], 200

我如何测试和我的期望

所以转到招摇页面,我希望定义的 2 个端点在那里,但我只看到上述错误。

只是在外壳中使用 Ipython,我尝试使用请求来跟踪调用并返回 404。

import json
import requests as r

base_url = 'http://127.0.0.1:5000/'
response = r.get(base_url + 'api/test')
response
response = r.get(base_url + 'api/test/')
response
data = json.dumps({'greeting': 'hi'})
response = r.post(base_url + 'test/custom', data=data)
response
data = json.dumps({'greeting': 'hi'})
response = r.post(base_url + 'test/custom/', data=data)
response
4

1 回答 1

2

TL;博士

我在代码和测试中犯了一些错误:

  1. 在声明路由之前注册 api。
  2. 对如何将参数传递给post方法做出奇怪的假设。
  3. expect在装饰器中使用模型而不是请求解析器
  4. 在我的测试中使用错误的api/前缀调用端点。

在全

我相信这是因为我在声明任何路由之前在 api 上注册了命名空间。

我的理解是,当 api 在应用程序上注册时,应用程序上的 swagger 文档和路由就在此时设置。因此,此后在 api 上定义的任何路由都无法识别。我认为这是因为当我在views/test.py文件中声明命名空间(也是避免此文件和 之间循环引用的模型views/__init__.py)时,swagger 文档定义了路由并且我的测试有效(在我更正它们之后)。

我的应用程序和测试中还有一些错误,它们是

进一步的错误1

在我的应用程序中,在views/test.py文件中,我做了一个愚蠢的假设,即变量将由预期的参数组成(我只是神奇地将问候语作为一些非局部变量)。查看文档,我了解了RequestParser,并且我需要像这样声明一个

from flask_restx import reqparse

# Parser
custom_greeting_parser = reqparse.RequestParser()
custom_greeting_parser.add_argument('greeting', required=True, location='json')

并在expect装饰器中使用它。post然后我可以在我的方法中检索参数字典。与以下

...
    def post(self):
        args = custom_greeting_parser.parse_args()
        greeting = args['greeting']
        ...

结果**kwargs证明是不必要的。

进一步的错误2

在我的测试中,我调用了端点api/test,这是不正确的,它只是test. 该端点的校正测试是

更正test端点测试

import json
import requests as r

base_url = 'http://127.0.0.1:5000/'

response = r.get(base_url + 'test')
print(response)
print(json.loads(response.content.decode()))

进一步的错误3

另一个端点的测试,即帖子,我需要包含一个声明内容类型的标头,以便解析器“看到”参数,因为我已将位置明确指定为 json。更正了下面的测试。

更正test/custom端点测试

import json
import requests as r

base_url = 'http://127.0.0.1:5000/'

data = json.dumps({'greeting': 'hi'})
headers = {'content-type': 'application/json'}
response = r.post(base_url + 'test/custom', data=data, headers=headers)
print(response)
print(json.loads(response.content.decode()))

更正的代码

对于代码不正确的文件。

意见/初始化.py

from flask_restx import Api

from project.views.test import ns_test


api = Api(
    title='TEST API',
    version='1.0',
    description='Testing Flask-RestX API.'
)


# Add namespaces
api.add_namespace(ns_test)

意见/test.py

from flask_restx import Resource, Namespace, fields, reqparse


# Namespace
ns_test = Namespace('test', description='a test namespace')

# Models
custom_greeting_model = ns_test.model('Custom', {
    'greeting': fields.String(required=True),
    'id': fields.Integer(required=True),
})

# Parser
custom_greeting_parser = reqparse.RequestParser()
custom_greeting_parser.add_argument('greeting', required=True, location='json')


custom_greetings = list()


@ns_test.route('/')
class Hello(Resource):

    @ns_test.doc('say_hello')
    def get(self):
        return 'hello', 200


@ns_test.route('/custom')
class Custom(Resource):

    @ns_test.doc('custom_hello')
    @ns_test.expect(custom_greeting_parser)
    @ns_test.marshal_with(custom_greeting_model)
    def post(self):
        args = custom_greeting_parser.parse_args()
        greeting = args['greeting']

        custom_greetings.append(greeting)
        pos = len(custom_greetings) - 1

        return [{'id': pos, 'greeting': greeting}], 200
于 2020-09-10T15:56:57.540 回答