简体   繁体   English

使用 Flask-RestX 制作 RESTful API 时遇到问题:“规范中没有定义操作!” 和“404”

[英]Having Trouble Making a RESTful API with Flask-RestX: “No operations defined in spec!” and “404”s

In summary, I have been following the flask restx tutorials to make an api, however none of my endpoints appear on the swagger page ("No operations defined in spec!") and I just get 404 whenever I call them总之,我一直在关注flask restx教程来制作一个api,但是我的端点都没有出现在swagger页面上(“规范中没有定义操作!”),每当我调用它们时我都会得到404

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

I'm using python 3.8.3 for reference.我正在使用 python 3.8.3 作为参考。

A cut down example of what I'm doing is as follows.我正在做的一个简化的例子如下。

My question in short is, what am I missing?简而言之,我的问题是,我错过了什么? Currently drawing blank on why this doesn't work.目前尚不清楚为什么这不起作用。

Directory Structure目录结构

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

File Contents文件内容

requirements.txt要求.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()

project/ init .py项目/初始化.py

from flask import Flask

from project.views import api


app = Flask(__name__)

api.init_app(app)

project/views/ init .py项目/视图/初始化.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项目/视图/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

How I'm Testing & What I Expect我的测试方式和我的期望

So going to the swagger page, I expect the 2 endpoints defined to be there, but I just see the aforementioned error.所以转到 swagger 页面,我希望定义的 2 个端点在那里,但我只看到上述错误。

Just using Ipython in a shell, I've tried to following calls using requests and just get back 404s.只是在 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 TL; 博士

I made a few mistakes in my code and test:我在代码和测试中犯了一些错误:

  1. Registering api before declaring the routes.在声明路由之前注册 api。
  2. Making a wierd assumption about how the arguments would be passed to the post method.对如何将参数传递给post方法做出一个奇怪的假设。
  3. Using a model instead of request parser in the expect decoratorexpect装饰器中使用模型而不是请求解析器
  4. Calling the endpoints in my testing with an erroneous api/ prefix.在我的测试中使用错误的api/前缀调用端点。

In Full在全

I believe it's because I registered the namespace on the api before declaring any routes.我相信这是因为我在声明任何路由之前在 api 上注册了命名空间。

My understanding is when the api is registered on the app, the swagger documentation and routes on the app are setup at that point.我的理解是,当 api 在应用程序上注册时,应用程序上的 swagger 文档和路由就在那时设置。 Thus any routes defined on the api after this are not recognised.因此,在此之后在 api 上定义的任何路由都不会被识别。 I think this because when I declared the namespace in the views/test.py file (also the model to avoid circular referencing between this file and views/__init__.py ), the swagger documentation had the routes defined and my tests worked (after I corrected them).我认为这是因为当我在views/test.py文件(也是避免此文件和views/__init__.py之间循环引用的模型)中声明命名空间时,swagger 文档定义了路由并且我的测试工作(在我之后纠正他们)。

There were some more mistakes in my app and my tests, which were我的应用程序和测试中还有一些错误,它们是

Further Mistake 1进一步的错误 1

In my app, in the views/test.py file, I made a silly assumption that a variable would be made of the expected parameter (that I would just magically have greeting as some non-local variable).在我的应用程序中,在views/test.py文件中,我做了一个愚蠢的假设,即一个变量将由预期的参数组成(我只是神奇地将greeting作为一些非局部变量)。 Looking at the documentation, I learnt about the RequestParser , and that I needed to declare one like so查看文档,我了解了RequestParser ,并且我需要像这样声明一个

from flask_restx import reqparse

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

and use this in the expect decorator.并在expect装饰器中使用它。 I could then retrieve a dictionary of the parameters in my post method.然后我可以在我的post方法中检索参数的字典。 with the below与下面

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

The **kwargs turned out to be unnecessary.事实证明**kwargs是不必要的。

Further Mistake 2进一步的错误 2

In my tests, I was calling the endpoint api/test , which was incorrect, it was just test .在我的测试中,我正在调用端点api/test ,这是不正确的,它只是test The corrected test for this endpoint is此端点的更正测试是

Corrected test for test endpoint更正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()))

Further Mistake 3进一步的错误 3

The test for the other endpoint, the post, I needed to include a header declaring the content type so that the parser would "see" the parameters, because I had specified the location explictily as json.对于另一个端点的测试,post,我需要包含一个声明内容类型的标头,以便解析器可以“看到”参数,因为我已经明确地将位置指定为 json。 Corrected test below.下面更正测试。

Corrected test for test/custom endpoint更正测试test/custom端点的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()))

Corrected Code更正代码

For the files with incorrect code.对于代码不正确的文件。

views/ init .py视图/初始化.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视图/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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM