Python. Cannot make a request to simple site api. Flask and requests - python

I am trying to create simple API for my site. I created the route with flask:
#api.route('/api/rate&message_id=<message_id>&performer=<performer_login>', methods=['POST'])
def api_rate_msg(message_id, performer_login):
print("RATE API ", message_id, ' ', performer_id)
return 400
print(...) function don't execute...
I use flask-socketio to communicate between client and server.
I send json from client and process it with:
#socket.on('rate')
def handle_rate(data):
print(data)
payload = {'message_id':data['message_id'], 'performer':data['performer']}
r = requests.post('/api/rate', params=payload)
print (r.status_code)
Note, that data variable is sending from client and is correct(I've checked it).
print(r.status_code) don't exec too...
Where I'm wrong? Please, sorry for my bad english :(
This api function must increase rate of message, which stored in mongodb, if interesting.

Don't put &message_id=<message_id>&performer=<performer_login> in your route string. Instead, get these arguments from request.args.
Try it:
from flask import request
...
#api.route('/api/rate', methods=['POST'])
def api_rate_msg():
print(request.args)
return ''
I've tested it with httpie:
$ http -v POST :5000/api/rate message_id==123 performer_login==foo
POST /api/rate?message_id=123&performer_login=foo HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:5000
User-Agent: HTTPie/0.9.8
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 02 Apr 2017 13:54:40 GMT
Server: Werkzeug/0.11.11 Python/2.7.13
And from flask's log:
ImmutableMultiDict([('message_id', u'123'), ('performer_login', u'foo')])
127.0.0.1 - - [02/Apr/2017 22:54:40] "POST /api/rate?message_id=123&performer_login=foo HTTP/1.1" 200 -

Remove the below part from your api route
&message_id=<message_id>&performer=<performer_login
This is not required in POST request. It helps in GET requests. API call in request is not matching the route definition and therefore you have the current problem

Related

flask - empty form data when sending request from file with netcat

I have a remote server running a simple flask server on it. I use this server for debugging purposes of my MCU cellular interface.
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route("/b61d39e9-94ca-49e2-96ba-bdc3325a8aeb", methods = ["POST"])
def root():
print(request.headers)
if(request.is_json):
content = request.get_json()
print (content)
print(request.form.get("uuid"))
return "OK"
if(__name__ == "__main__"):
app.run(host = "0.0.0.0", port = 65432, debug = True)
And I send this request using netcat and the command nc ... 65432 < Request.txt
POST /b61d39e9-94ca-49e2-96ba-bdc3325a8aeb HTTP/1.1
Host: ESP32-DevKit VE
Content-Type: multipart/form-data;boundary="data"
--data
Content-Disposition: form-data; name="uuid"
ff0dbd36-dc36-4df5-8196-324adf76ed3f
--data--
netcat is reporting error 200, so everything was fine, but the flask web server doesn´t output the value of the field uuid.
... - - [13/Aug/2022 09:21:21] "POST /b61d39e9-94ca-49e2-96ba-bdc3325a8aeb HTTP/1.1" 200 -
Host: ESP32-DevKit VE
Content-Type: multipart/form-data;boundary="data"
None
I can not figure out what kind of problem is here.
It appears you're missing Content-Length header. I was able to get uuid using your python code above and this request:
POST /b61d39e9-94ca-49e2-96ba-bdc3325a8aeb HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------data
Content-Length: 135
----------------------------data
Content-Disposition: form-data; name="uuid"
ff0dbd36-dc36-4df5-8196-324adf76ed3f
----------------------------data--

Return data on HTTP upload to sanic server

I'm experimenting with uploading data to a sanic web server. To do so I issue a POST request using curl. I try to return some data after the POST request. The rationale behind this is to return some ID now representing the upload on the server side. But this doesn't seem to work. Now I'm wondering: Is my program wrong? Does curl not write the output? Or is this a bug in sanic? Could anyone help me here? Thanks!
Here's the Python program:
import signal
import asyncio
import uvloop
import sanic
app = sanic.Sanic(__name__)
loop = asyncio.get_event_loop()
server = app.create_server(host="localhost", port=3002)
task = asyncio.ensure_future(server)
#app.post("/testUpload", stream=True)
async def api_testUpload(request):
async def doStream(response):
while True:
body = await request.stream.get()
if body is None:
break
sanic.response.json({
"result": "good!"
})
return sanic.response.stream(doStream)
signal.signal(signal.SIGINT, lambda s, f: loop.stop())
try:
loop.run_forever()
except:
loop.stop()
You can invoke curl like this:
curl -v --data-binary "#somefile.data" http://localhost:3002/testUpload
And here is what curl writes to STDOUT:
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3002 (#0)
> POST /testUpload HTTP/1.1
> Host: localhost:3002
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 334504
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 200 OK
< Keep-Alive: 5
< Transfer-Encoding: chunked
< Content-Type: text/plain; charset=utf-8
<
* Done waiting for 100-continue
* We are completely uploaded and fine
* Connection #0 to host localhost left intact
As you can see, a text/plain response is generated. This should be an application/json with my data, shouldn't it?
This is not the right way to do it. The sanic API does not work that intuitively here. In order to write data after receiving the bytes sent you have to do it this way within the streaming function doStream():
await response.write(json.dumps({
"result": "good!"
}))
If I understand your question, then you do not need to stream at all. It seems to me like what you are attempting to do is:
upload a file
process the file and do something
return a response to say that something was done
Assuming this is the case, then you do not need to stream at all.
import sanic
app = sanic.Sanic(__name__)
def do_something(file):
print(f'doing something to {file.name}')
#app.post("/testUpload")
async def api_testUpload(request):
myfile = request.files.get('myfile')
do_something(myfile)
return sanic.response.json({
'body': myfile.body,
'name': myfile.name,
'type': myfile.type,
})
app.run(host='localhost', port=3002, auto_reload=True)
You can then hit your endpoint with a file ...
curl -i -F "myfile=#/tmp/somefile.txt" http://localhost:3002/testUpload
And you should see in your logs:
[2018-10-25 10:09:56 +0300] [16051] [INFO] Goin' Fast # http://localhost:3002
[2018-10-25 10:09:56 +0300] [16055] [INFO] Starting worker [16055]
doing something to somefile.txt
[2018-10-25 10:09:59 +0300] - (sanic.access)[INFO][127.0.0.1:39306]: POST http://localhost:3002/testUpload 200 62
And your return
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: 5
Content-Length: 62
Content-Type: application/json
{"body":"FOOBAR\n","name":"somefile.txt","type":"text\/plain"}
Now, I suppose if you are uploading a file, and you wanted to stream the response of that upload as it was happening to that request or another, that would be a different issue.
I hope this helps.

HTTP post Json 400 Error

I am trying to post data to my server from my microcontroller. I need to send raw http data from my controller and this is what I am sending below:
POST /postpage HTTP/1.1
Host: https://example.com
Accept: */*
Content-Length: 18
Content-Type: application/json
{"cage":"abcdefg"}
My server requires JSON encoding and not form encoded request.
For the above request sent, I get an 400 error from the server, HTTP/1.1 400 Bad Request
However, when I try to reach the post to my server via a python script via my laptop, I am able to get a proper response.
import requests
url='https://example.com'
mycode = 'abcdefg'
def enter():
value = requests.post('url/postpage',
params={'cage': mycode})
print vars(value)
enter()
Can anyone please let me know where I could be going wrong in the raw http data I'm sending above ?
HTTP specifies the separator between headers as a single newline, and requires a double newline before the content:
POST /postpage HTTP/1.1
Host: https://example.com
Accept: */*
Content-Length: 18
Content-Type: application/json
{"cage":"abcdefg"}
If you don’t think you’ve got all of the request right, try seeing what was sent by Python:
response = ...
request = response.request # request is a PreparedRequest.
headers = request.headers
url = request.url
Read the docs for PreparedRequest for more information.
To pass a parameter, use this Python:
REQUEST = 'POST /postpage%s HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: python-requests/2.4.3 CPython/2.7.9 Linux/4.4.11-v7+\r\n\r\n';
query = ''
for k, v in params.items():
query += '&' + k + '=' + v # URL-encode here if you want.
if len(query): query = '?' + query[1:]
return REQUEST % query

abort method in Flask-Restful ignoring CORS options

I have a Flask-Restful API configured with some CORS options:
api = Api()
api.decorators=[cors.crossdomain(origin='*', headers=['accept', 'Content-Type'])]
...
api.init_app(app)
My API accepts POST requests which may fail if the data in the request is invalid:
class myAPI(Resource):
def post(self):
args = request.get_json()
if args.get('something'):
return {'message': 'Request worked, data received!',
'something': args['something']}
else:
abort(500, "Error: Data must contain a 'something' field!")
When I make a successful POST request to my API I can see that the CORS options are properly set:
...
* upload completely sent off: 81 out of 81 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 205
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: HEAD, GET, POST, OPTIONS
< Access-Control-Max-Age: 21600
< Access-Control-Allow-Headers: ACCEPT, CONTENT-TYPE
< Server: Werkzeug/0.9.4 Python/2.7.6
If, however, the post call in my class exits through the abort method (by purposely sending bad data to the request) then the Access-Control-* fields are all missing from the response:
* upload completely sent off: 75 out of 75 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 500 INTERNAL SERVER ERROR
< Content-Type: application/json
< Content-Length: 51
< Server: Werkzeug/0.9.4 Python/2.7.6
Is it possible to make the abort method play nice with my CORS rules, or should I create my own full-fledged response and avoid using the abort function?
When you trigger an abort, the error handler registered to the HTTP error code is automatically invoked, and the response is not actually served by your view function.
If you use the CORS middleware which is provided by Flask-Cors, instead of the decorator form, in the case of handled exceptions and aborts, the CORS headers will be injected as expected.
If an unhandled exception occurs, (E.G. there is an error in your code, and a 500 internal server error), Flask bypasses middleware, and after_request handlers are not run.
Full disclosure, I wrote Flask-Cors.

Bottle framework with a tunnel doesn't receive GET arguments

I'm trying to make a simple python script which returns an argument from GET request. The issue is that it does not receive any arguments and returns blank body. There is one peculiar thing, though. In order to test GET requests I use requestmaker.com, hurl.it and apikitchen.com. While requestmaker and apikitchen return an empty body, hurl.it actually returns the required parameter.
I have tried Bottle, Flask and Tornado with the same results. I'm using ngrok for tunneling but I've also tried forwardhq.com.
The code (with bottle framework):
import bottle
from bottle import route, run, request, response
bottle.debug(True)
#route('/')
def home():
return "Great Scott!"
#route('/valley')
def thevalley():
theflux = request.query.flux
return theflux
run(host='0.0.0.0', port=8515, reloader=True)
ngrok status:
Tunnel Status online
Version 1.6/1.5
Forwarding http://88mph.ngrok.com -> 127.0.0.1:8515
The results I get from GET requests:
requestmaker.com
Request Headers Sent:
GET /valley HTTP/1.1
Host: 88mph.ngrok.com
Accept: */*
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
Response Headers:
HTTP/1.1 200 OK
Server: nginx/1.4.3
Date: Sun, 09 Feb 2014 13:51:20 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: keep-alive
bottle:
127.0.0.1 - - [09/Feb/2014 13:51:20] "GET /valley HTTP/1.1" 200 0
hurl.it
Request:
Accept: */*
Accept-Encoding: gzip, deflate, compress
User-Agent: runscope/0.1
Response:
Connection: keep-alive
Content-Length: 5
Content-Type: text/html; charset=UTF-8
Date: Sun, 09 Feb 2014 14:02:55 GMT
Server: nginx/1.4.3
Body:
121gw
bottle:
127.0.0.1 - - [09/Feb/2014 14:02:55] "GET /valley?flux=121gw HTTP/1.1" 200 5
https://88mph.ngrok.com/valley?flux=121gw
Finally, just entering the URL into the address bar works as well, I get "121gw".
bottle:
127.0.0.1 - - [09/Feb/2014 14:05:46] "GET /valley?flux=121gw HTTP/1.1" 200 5
End
Every request maker can connect to server (200 OK) and even return "Great Scott" when accessing root. However, only webbrowser and hurl return the argument. Any ideas what is at fault here?

Categories