使用 Flask-RestX 制作 RESTful API 时遇到问题:"No operations defined in spec!" 和“404”
Having Trouble Making a RESTful API with Flask-RestX: "No operations defined in spec!" and "404"s
总而言之,我一直在按照 flask restx 教程制作一个 api,但是 none 我的端点出现在 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()
项目/init.py
from flask import Flask
from project.views import api
app = Flask(__name__)
api.init_app(app)
project/views/init.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
我如何测试以及我期望什么
所以转到 swagger 页面,我希望定义的 2 个端点在那里,但我只看到上述错误。
仅在 shell 中使用 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
我在代码和测试中犯了一些错误:
- 在声明路由之前注册 api。
- 对如何将参数传递给
post
方法做出奇怪的假设。
- 在
expect
装饰器中使用模型而不是请求解析器
- 在我的测试中使用错误的
api/
前缀调用端点。
完整
我相信这是因为我在声明任何路由之前在 api 上注册了命名空间。
我的理解是,当 api 在应用程序上注册时,应用程序上的 swagger 文档和路由会在此时设置。因此,在此之后在 api 上定义的任何路由都不会被识别。我认为这是因为当我在 views/test.py
文件(也是避免此文件和 views/__init__.py
之间的循环引用的模型)中声明名称空间时,swagger 文档定义了路由并且我的测试有效(在我之后更正了它们)。
我的应用和测试中还有一些错误,这些错误是
进一步的错误 1
在我的应用程序中,在 views/test.py
文件中,我做了一个愚蠢的假设,即变量将由预期参数组成(我会神奇地拥有 greeting 作为一些 non-local 变量)。查看文档,我了解了 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 的测试,我需要包含一个 header 声明内容类型,以便解析器可以“看到”参数,因为我已经指定了位置显式为 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()))
更正代码
对于代码不正确的文件。
views/init.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', 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
总而言之,我一直在按照 flask restx 教程制作一个 api,但是 none 我的端点出现在 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()
项目/init.py
from flask import Flask
from project.views import api
app = Flask(__name__)
api.init_app(app)
project/views/init.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
我如何测试以及我期望什么
所以转到 swagger 页面,我希望定义的 2 个端点在那里,但我只看到上述错误。
仅在 shell 中使用 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
我在代码和测试中犯了一些错误:
- 在声明路由之前注册 api。
- 对如何将参数传递给
post
方法做出奇怪的假设。 - 在
expect
装饰器中使用模型而不是请求解析器 - 在我的测试中使用错误的
api/
前缀调用端点。
完整
我相信这是因为我在声明任何路由之前在 api 上注册了命名空间。
我的理解是,当 api 在应用程序上注册时,应用程序上的 swagger 文档和路由会在此时设置。因此,在此之后在 api 上定义的任何路由都不会被识别。我认为这是因为当我在 views/test.py
文件(也是避免此文件和 views/__init__.py
之间的循环引用的模型)中声明名称空间时,swagger 文档定义了路由并且我的测试有效(在我之后更正了它们)。
我的应用和测试中还有一些错误,这些错误是
进一步的错误 1
在我的应用程序中,在 views/test.py
文件中,我做了一个愚蠢的假设,即变量将由预期参数组成(我会神奇地拥有 greeting 作为一些 non-local 变量)。查看文档,我了解了 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 的测试,我需要包含一个 header 声明内容类型,以便解析器可以“看到”参数,因为我已经指定了位置显式为 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()))
更正代码
对于代码不正确的文件。
views/init.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', 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