Возникли проблемы с созданием RESTful API с помощью Flask-RestX: "В спецификации нет операций!" и "404" с
Таким образом, я следовал руководствам flask restx, чтобы создать api, однако ни одна из моих конечных точек не отображается на странице swagger ("В спецификации нет операций, определенных в спецификации!"), И я просто получаю 404 каждый раз, когда я вызываю их
Я создал свой api в основном после этого https://flask-restx.readthedocs.io/en/latest/scaling.html
Я использую 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)
проект / просмотров /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)
проект / просмотров / 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, я ожидаю, что там будут две определенные конечные точки, но я просто вижу вышеупомянутую ошибку.
Просто используя 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
1 ответ
TL;DR
Я сделал несколько ошибок в своем коде и проверил:
- Регистрация api перед объявлением маршрутов.
- Сделав странное предположение о том, как аргументы будут передаваться в
post
метод. - Использование модели вместо парсера запросов в
expect
декоратор - Вызов конечных точек в моем тестировании с ошибочным
api/
приставка.
В полном объеме
Я считаю, что это потому, что я зарегистрировал пространство имен в api перед объявлением каких-либо маршрутов.
Насколько я понимаю, когда api зарегистрирован в приложении, в этот момент настраиваются документация по swagger и маршруты в приложении. Таким образом, любые маршруты, определенные в api после этого, не распознаются. Я думаю это потому, что когда я объявил пространство имен в
views/test.py
файл (также модель, чтобы избежать циклических ссылок между этим файлом и
views/__init__.py
), в документации по чванству были определены маршруты и мои тесты работали (после того, как я их исправил).
В моем приложении и моих тестах было еще несколько ошибок, которые
Дальнейшая ошибка 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()))
Исправленный код
Для файлов с некорректным кодом.
просмотров /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)
просмотров /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