Я новичок в Flasgger и пытаюсь понять, как добавить к нему POST-запрос.
Вот мое приложение Flask с Flasgger (app.py
):
import datetime
import numpy as np
import pandas as pd
from flask import Flask, jsonify, make_response, Response, request, json
from flasgger import Swagger
def upload_data(file):
df = pd.read_json(file)
df['Rate'] = pd.to_datetime(df['Rate'], format = "%Y-%d-%m %H:%M:%S")
df['SI'] = np.nan
return df
server = Flask(__name__)
Swagger(server)
@server.route('/results', methods=['POST', 'GET'])
def ML_results_get():
"""post endpoint
---
tags:
- ML-KA-results
parameters:
- name: body
in: body
required: true
schema:
id: ML-KA-results
type: "object"
required:
- Rate
- KE
- SU
- ML-PC 1
- ML-PC 2
- ML-PC 3
- ML-PC 4
- ML-PC 5
- ML-PC 6
- ML-Totals
- LL-PC 1
- LL-PC 2
- LL-PC 3
- LL-PC 4
- LL-PC 5
- LL-PC 6
- LL-Totals
properties:
Rate:
type: string
default: "9/20/2020 8:50"
KE:
type: string
default: "1"
SU:
type: string
default: " GgG"
ML-PC 1:
type: string
default: "154.11"
ML-PC 2:
type: string
default: "0"
ML-PC 3:
type: string
default: "0"
ML-PC 4:
type: string
default: "0"
ML-PC 5:
type: string
default: "0"
ML-PC 6:
type: string
default: "0"
ML-Totals:
type: string
default: "154.11"
LL-PC 1:
type: string
default: "0"
LL-PC 2:
type: string
default: "0"
LL-PC 3:
type: string
default: "0"
LL-PC 4:
type: string
default: "0"
LL-PC 5:
type: string
default: "0"
LL-PC 6:
type: string
default: "0"
LL-Totals:
type: string
default: "0"
responses:
200:
description: The product inserted in the database
schema:
$ref: '#/definitions/Product'
"""
with server.test_request_context('/results'):
if request.method == 'POST':
reqData = request.get_json(force=True)
df_ML = upload_data(reqData)
return df_ML.to_json()
else:
return print('GET is not supported')
if __name__ == '__main__':
server.run(debug=True, port=85)
Теперь я хочу попробовать, как работает Flasgger. Когда я захожу на http://localhost:85/apidocs/#/ML-KA-results/post_results, нажимаю «Попробовать». и скопируйте следующий файл json:
[
{
"Rate": "9/20/2020 8:50",
"KE": 1,
"SU": " GgG",
"ML-PC 1": 154.11,
"ML-PC 2": 0,
"ML-PC 3": 0,
"ML-PC 4": 0,
"ML-PC 5": 0,
"ML-PC 6": 0,
"ML-Totals": 154.11,
"LL-PC 1": 0,
"LL-PC 2": 0,
"LL-PC 3": 0,
"LL-PC 4": 0,
"LL-PC 5": 0,
"LL-PC 6": 0,
"LL-Totals": 0
},
{
"Rate": "9/20/2020 8:50",
"KE": 2,
"SU": " GgG",
"ML-PC 1": 154.27,
"ML-PC 2": 0,
"ML-PC 3": 0,
"ML-PC 4": 0,
"ML-PC 5": 0,
"ML-PC 6": 0,
"ML-Totals": 154.27,
"LL-PC 1": 0,
"LL-PC 2": 0,
"LL-PC 3": 0,
"LL-PC 4": 0,
"LL-PC 5": 0,
"LL-PC 6": 0,
"LL-Totals": 0
}
]
Когда я нажимаю «Выполнить», я получаю следующую ошибку:
Error: INTERNAL SERVER ERROR
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement. // Werkzeug Debugger</title>
<link rel = "stylesheet" href = "?__debugger__=yes&cmd=resource&f=style.css"
type = "text/css">
<!-- We need to make sure this has a favicon so that the debugger does
not by accident trigger a request to /favicon.ico which might
change the application state. -->
<link rel = "shortcut icon"
href = "?__debugger__=yes&cmd=resource&f=console.png">
<script src = "?__debugger__=yes&cmd=resource&f=jquery.js"></script>
<script src = "?__debugger__=yes&cmd=resource&f=debugger.js"></script>
<script type = "text/javascript">
var TRACEBACK = 2439966170584,
CONSOLE_MODE = false,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "AhMBlga2HAcS1uO5N5Tz";
</script>
</head>
<body style = "background-color: #fff">
<div class = "debugger">
<h1>TypeError</h1>
<div class = "detail">
<p class = "errormsg">TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.</p>
</div>
<h2 class = "traceback">Traceback <em>(most recent call last)</em></h2>
<div class = "traceback">
<ul><li><div class = "frame" id = "frame-2439967649920">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">2464</em>,
in <code class = "function">__call__</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"></span> </pre>
<pre class = "line before"><span class = "ws"> </span>def __call__(self, environ, start_response):</pre>
<pre class = "line before"><span class = "ws"> </span>"""The WSGI server calls the Flask application object as the</pre>
<pre class = "line before"><span class = "ws"> </span>WSGI application. This calls :meth:`wsgi_app` which can be</pre>
<pre class = "line before"><span class = "ws"> </span>wrapped to applying middleware."""</pre>
<pre class = "line current"><span class = "ws"> </span>return self.wsgi_app(environ, start_response)</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"> </span>def __repr__(self):</pre>
<pre class = "line after"><span class = "ws"> </span>return "<%s %r>" % (self.__class__.__name__, self.name)</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650760">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">2450</em>,
in <code class = "function">wsgi_app</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>try:</pre>
<pre class = "line before"><span class = "ws"> </span>ctx.push()</pre>
<pre class = "line before"><span class = "ws"> </span>response = self.full_dispatch_request()</pre>
<pre class = "line before"><span class = "ws"> </span>except Exception as e:</pre>
<pre class = "line before"><span class = "ws"> </span>error = e</pre>
<pre class = "line current"><span class = "ws"> </span>response = self.handle_exception(e)</pre>
<pre class = "line after"><span class = "ws"> </span>except: # noqa: B001</pre>
<pre class = "line after"><span class = "ws"> </span>error = sys.exc_info()[1]</pre>
<pre class = "line after"><span class = "ws"> </span>raise</pre>
<pre class = "line after"><span class = "ws"> </span>return response(environ, start_response)</pre>
<pre class = "line after"><span class = "ws"> </span>finally:</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650032">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">1867</em>,
in <code class = "function">handle_exception</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span># if we want to repropagate the exception, we can attempt to</pre>
<pre class = "line before"><span class = "ws"> </span># raise it with the whole traceback in case we can do that</pre>
<pre class = "line before"><span class = "ws"> </span># (the function was actually called from the except part)</pre>
<pre class = "line before"><span class = "ws"> </span># otherwise, we just raise the error again</pre>
<pre class = "line before"><span class = "ws"> </span>if exc_value is e:</pre>
<pre class = "line current"><span class = "ws"> </span>reraise(exc_type, exc_value, tb)</pre>
<pre class = "line after"><span class = "ws"> </span>else:</pre>
<pre class = "line after"><span class = "ws"> </span>raise e</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"> </span>self.log_exception((exc_type, exc_value, tb))</pre>
<pre class = "line after"><span class = "ws"> </span>server_error = InternalServerError()</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650144">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\_compat.py"</cite>,
line <em class = "line">39</em>,
in <code class = "function">reraise</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>import collections.abc as collections_abc</pre>
<pre class = "line before"><span class = "ws"></span> </pre>
<pre class = "line before"><span class = "ws"> </span>def reraise(tp, value, tb=None):</pre>
<pre class = "line before"><span class = "ws"> </span>if value.__traceback__ is not tb:</pre>
<pre class = "line before"><span class = "ws"> </span>raise value.with_traceback(tb)</pre>
<pre class = "line current"><span class = "ws"> </span>raise value</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"> </span>implements_to_string = _identity</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"></span>else:</pre>
<pre class = "line after"><span class = "ws"> </span>iterkeys = lambda d: d.iterkeys()</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650872">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">2447</em>,
in <code class = "function">wsgi_app</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>ctx = self.request_context(environ)</pre>
<pre class = "line before"><span class = "ws"> </span>error = None</pre>
<pre class = "line before"><span class = "ws"> </span>try:</pre>
<pre class = "line before"><span class = "ws"> </span>try:</pre>
<pre class = "line before"><span class = "ws"> </span>ctx.push()</pre>
<pre class = "line current"><span class = "ws"> </span>response = self.full_dispatch_request()</pre>
<pre class = "line after"><span class = "ws"> </span>except Exception as e:</pre>
<pre class = "line after"><span class = "ws"> </span>error = e</pre>
<pre class = "line after"><span class = "ws"> </span>response = self.handle_exception(e)</pre>
<pre class = "line after"><span class = "ws"> </span>except: # noqa: B001</pre>
<pre class = "line after"><span class = "ws"> </span>error = sys.exc_info()[1]</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650928">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">1953</em>,
in <code class = "function">full_dispatch_request</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>rv = self.preprocess_request()</pre>
<pre class = "line before"><span class = "ws"> </span>if rv is None:</pre>
<pre class = "line before"><span class = "ws"> </span>rv = self.dispatch_request()</pre>
<pre class = "line before"><span class = "ws"> </span>except Exception as e:</pre>
<pre class = "line before"><span class = "ws"> </span>rv = self.handle_user_exception(e)</pre>
<pre class = "line current"><span class = "ws"> </span>return self.finalize_request(rv)</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"> </span>def finalize_request(self, rv, from_error_handler=False):</pre>
<pre class = "line after"><span class = "ws"> </span>"""Given the return value from a view function this finalizes</pre>
<pre class = "line after"><span class = "ws"> </span>the request by converting it into a response and invoking the</pre>
<pre class = "line after"><span class = "ws"> </span>postprocessing functions. This is invoked for both normal</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967650984">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">1968</em>,
in <code class = "function">finalize_request</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>with the `from_error_handler` flag. If enabled, failures in</pre>
<pre class = "line before"><span class = "ws"> </span>response processing will be logged and otherwise ignored.</pre>
<pre class = "line before"><span class = "ws"></span> </pre>
<pre class = "line before"><span class = "ws"> </span>:internal:</pre>
<pre class = "line before"><span class = "ws"> </span>"""</pre>
<pre class = "line current"><span class = "ws"> </span>response = self.make_response(rv)</pre>
<pre class = "line after"><span class = "ws"> </span>try:</pre>
<pre class = "line after"><span class = "ws"> </span>response = self.process_response(response)</pre>
<pre class = "line after"><span class = "ws"> </span>request_finished.send(self, response=response)</pre>
<pre class = "line after"><span class = "ws"> </span>except Exception:</pre>
<pre class = "line after"><span class = "ws"> </span>if not from_error_handler:</pre></div>
</div>
<li><div class = "frame" id = "frame-2439967649864">
<h4>File <cite class = "filename">"C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py"</cite>,
line <em class = "line">2098</em>,
in <code class = "function">make_response</code></h4>
<div class = "source "><pre class = "line before"><span class = "ws"> </span>)</pre>
<pre class = "line before"><span class = "ws"></span> </pre>
<pre class = "line before"><span class = "ws"> </span># the body must not be None</pre>
<pre class = "line before"><span class = "ws"> </span>if rv is None:</pre>
<pre class = "line before"><span class = "ws"> </span>raise TypeError(</pre>
<pre class = "line current"><span class = "ws"> </span>"The view function did not return a valid response. The"</pre>
<pre class = "line after"><span class = "ws"> </span>" function either returned None or ended without a return"</pre>
<pre class = "line after"><span class = "ws"> </span>" statement."</pre>
<pre class = "line after"><span class = "ws"> </span>)</pre>
<pre class = "line after"><span class = "ws"></span> </pre>
<pre class = "line after"><span class = "ws"> </span># make sure the body is an instance of the response class</pre></div>
</div>
</ul>
<blockquote>TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.</blockquote>
</div>
<div class = "plain">
<form action = "/?__debugger__=yes&cmd=paste" method = "post">
<p>
<input type = "hidden" name = "language" value = "pytb">
This is the Copy/Paste friendly version of the traceback. <span
class = "pastemessage">You can also paste this traceback into
a <a href = "https://gist.github.com/">gist</a>:
<input type = "submit" value = "create paste"></span>
</p>
<textarea cols = "50" rows = "10" name = "code" readonly>Traceback (most recent call last):
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1953, in full_dispatch_request
return self.finalize_request(rv)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1968, in finalize_request
response = self.make_response(rv)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2098, in make_response
"The view function did not return a valid response. The"
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.</textarea>
</form>
</div>
<div class = "explanation">
The debugger caught an exception in your WSGI application. You can now
look at the traceback which led to the error. <span class = "nojavascript">
If you enable JavaScript you can also use additional features such as code
execution (if the evalex feature is enabled), automatic pasting of the
exceptions and much more.</span>
</div>
<div class = "footer">
Brought to you by <strong class = "arthur">DON'T PANIC</strong>, your
friendly Werkzeug powered traceback interpreter.
</div>
</div>
<div class = "pin-prompt">
<div class = "inner">
<h3>Console Locked</h3>
<p>
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server.
<form>
<p>PIN:
<input type=text name=pin size=14>
<input type=submit name=btn value = "Confirm Pin">
</form>
</div>
</div>
</body>
</html>
<!--
Traceback (most recent call last):
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1953, in full_dispatch_request
return self.finalize_request(rv)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1968, in finalize_request
response = self.make_response(rv)
File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 2098, in make_response
"The view function did not return a valid response. The"
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
Как правильно написать Flasgger API?
Параметр, вероятно, должен быть определен как in: body
(не in: path
) и иметь schema
— см. этот пример. Это решает проблему?
@furas Спасибо, я написал полное сообщение об ошибке
@Helen Спасибо, я меняю параметры, как вы рекомендовали, но получаю еще одну ошибку. Я обновил свой вопрос с новыми параметрами.
Я никогда не использовал Flasgger
, но как по мне, ваша проблема в том, что вы используете строку with server.test_request_context('/results'):
на сервере. Его следует использовать не на сервере, а в клиенте для тестирования сервера. Если я удалю эту единственную строку (и оставлю остальные if request.method == 'POST': ...
), то это перестанет показывать эту проблему, но покажет другую проблему, потому что у вас ошибка с pandas
. Вы получаете данные с помощью requests.get_json()
и используете их в upload_data
в pd.read_json(file)
, но read_json()
ожидает имя файла или буфера, поэтому вы должны сохранить данные в файле и использовать его имя или использовать io.StringIO
Я никогда не использовал Flasgger
, но что касается меня, ваша проблема в том, что вы используете линию
with server.test_request_context('/results'):
на сервере.
Его следует использовать не на сервере, а в клиенте для тестирования сервера.
Если я удалю эту единственную строку (и оставлю остальные if request.method == 'POST': ...
), эта проблема перестанет отображаться.
Но тогда это показывает другую проблему, потому что у вас ошибка с pandas
. requests.get_json()
получает строку с данными (JSON
) и преобразует в словарь или список, но pd.read_json()
снова нужна строка с данными (JSON
), поэтому вам придется преобразовать ее обратно
data = request.get_json(force=True)
json_text = json.dumps(data)
df = pd.read_json(json_text, orient='records') #orient='index')
Или вы должны получить прямую строку без привязки
json_text = request.get_data()
df = upload_data(json_text)
Я встретил и другую проблему. Когда я нажимаю Try out
, тогда в примере отображаются данные для одной строки, и для этого нужно orient='index'
в read_json()
, но вы используете список со строками, и это нужно orient='records'
в read_json()
Полный рабочий код для примера данных со списком строк
import datetime
import numpy as np
import pandas as pd
from flask import Flask, jsonify, make_response, Response, request, json
from flasgger import Swagger
import io
def upload_data(json_text):
# `record` for list of rows, `index` for single row
df = pd.read_json(json_text, orient='records') #orient='index')
df['Date'] = pd.to_datetime(df['Date'], format = "%Y-%d-%m %H:%M:%S")
df['QI'] = np.nan
return df
server = Flask(__name__)
Swagger(server)
@server.route('/results', methods=['POST', 'GET'])
def DL_results_get():
"""post endpoint
---
tags:
- ML-KA-results
parameters:
- name: body
in: body
required: true
schema:
id: ML-KA-results
type: "object"
required:
- Rate
- KE
- SU
- ML-PC 1
- ML-PC 2
- ML-PC 3
- ML-PC 4
- ML-PC 5
- ML-PC 6
- ML-Totals
- LL-PC 1
- LL-PC 2
- LL-PC 3
- LL-PC 4
- LL-PC 5
- LL-PC 6
- LL-Totals
properties:
Rate:
type: string
default: "9/20/2020 8:50"
KE:
type: string
default: "1"
SU:
type: string
default: " GgG"
ML-PC 1:
type: string
default: "154.11"
ML-PC 2:
type: string
default: "0"
ML-PC 3:
type: string
default: "0"
ML-PC 4:
type: string
default: "0"
ML-PC 5:
type: string
default: "0"
ML-PC 6:
type: string
default: "0"
ML-Totals:
type: string
default: "154.11"
LL-PC 1:
type: string
default: "0"
LL-PC 2:
type: string
default: "0"
LL-PC 3:
type: string
default: "0"
LL-PC 4:
type: string
default: "0"
LL-PC 5:
type: string
default: "0"
LL-PC 6:
type: string
default: "0"
LL-Totals:
type: string
default: "0"
responses:
200:
description: The product inserted in the database
schema:
$ref: '#/definitions/Product'
"""
#with server.test_request_context('/results'):
if request.method == 'POST':
#data = request.get_json(force=True)
#json_text = json.dumps(data)
json_text = request.get_data()
df = upload_data(json_text)
return df.to_json()
else:
return print('GET is not supported')
if __name__ == '__main__':
server.run(debug=True, port=8500)
всегда помещайте полное сообщение об ошибке (начинающееся со слова «Traceback») в вопрос (не комментарий) в виде текста (не снимка экрана). Есть и другая полезная информация.