使用Flask-RestX制作RESTful API时遇到麻烦:“规范中未定义任何操作!”和“ 404” 目录结构文件内容我的测试方式和期望进一步的错误1 进一步的错误2 进一步的错误3

问题描述

总而言之,我一直在遵循flask 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

文件内容

requirements.txt

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

manage.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)

project / views / 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

解决方法

TL; DR

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

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

完整

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

我的理解是,当在应用程序上注册api时,就在该位置上设置了草率文档和路由。因此,此后在api上定义的任何路由都不会被识别。我想这是因为,当我在views/test.py文件(也是避免在此文件和views/__init__.py之间进行循环引用的模型)中声明名称空间时,swagger文档已定义了路由,并且我的测试成功了(更正它们。)

我的应用程序和测试中还有更多错误,

进一步的错误1

在我的应用程序的views/test.py文件中,我做出了一个愚蠢的假设,即变量将由预期参数组成(我会神奇地以 greeting 作为某些非局部变量)。通过查看文档,我了解了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

对于另一个端点(即post)的测试,我需要包括一个声明内容类型的标头,以便解析器可以“看到”参数,因为我已将位置明确指定为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)

views / 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',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