Возникли проблемы с созданием 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

Я сделал несколько ошибок в своем коде и проверил:

  1. Регистрация api перед объявлением маршрутов.
  2. Сделав странное предположение о том, как аргументы будут передаваться в post метод.
  3. Использование модели вместо парсера запросов в expect декоратор
  4. Вызов конечных точек в моем тестировании с ошибочным 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
Другие вопросы по тегам