How to read file sent from curl to Flask? - python

Flask code -
#app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
f = open(filename,'r')
l = f.readlines()
f.close()
return len(l)
On running, I get error as -
curl -H "Content-Type:application/json" -X POST http://127.0.0.1:5000/messages --data filename=#hello.json
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Am I accessing the curl param wrong (filename)? Or I am sending the file in wrong way?
Also Upload a file to a python flask server using curl
Tried doing
f = request.files['filename']
Still, same error.

What your curl command code is doing is reading the file hello.json and putting it in the body of the request. (This feature is actually very useful if you have a large chunk of JSON you need to send to the server).
Normally in application/json requests you send the JSON as the body of the request, so this may be what you want. You can use request.get_json to get this data as a Python dictionary.
If you want to upload an actual file - like uploading a picture - you want multi part form encoding, which you tell curl to send via the -F parameter. (See also: an SO answer about this: https://stackoverflow.com/a/12667839/224334 ).

Related

Passing a remote file to a http post request in python

I have a rest api that offers an upload file post functionality. To test this, I simply did this on my python test code:
fo = open('file.zip', 'rb')
rb_file = {'confile': ('file.zip', fo, 'multipart/form-data')}
resp = requests.post(url,files=rb_file)
fo.close()
This request returns a uuid response {ce9f2d23-8ecd-4c60-9d31-aef0be103d44} that is needed for initiating another post run for a scan.
From Swagger, manually passing this uuid to the scan post request generates the following curl:
curl -X POST "http://....../scan" -H "Content-Type: multipart/form-data" -F "FilID=ce9f2d23-8ecd-4c60-9d31-aef0be103d44"
My question is how to convert this curl to a python request code noting that I no longer have the file on my local machine. The server seems to be expecting a -F rather than a param. How do I pass a file that doesn't exist on my local machine to http request in this case? All I have is the filID. I tried running as param but that doesn't find the resource.
try this !
import requests
headers = {
'Content-Type': 'multipart/form-data',
}
files = {
'FilID': (None, 'ce9f2d23-8ecd-4c60-9d31-aef0be103d44'),
}
response = requests.post('http://....../scan', headers=headers, files=files)

Replacing curl with python requests equivalent to POST a file to a server

After writing a file with the snippet below
with open("temp.trig", "wb") as f:
f.write(data)
I use curl to load it into the server
curl -X POST -F file=#"temp.trig" -H "Accept: application/json" http://localhost:8081/demo/upload
which works fine.
I am trying to replace the curl with python requests, as follows:
with open("temp.trig", "rb") as f:
result = requests.post("http://localhost:8081/demo/upload", files={'file': f},
headers = {"Accept": "application/json"})
which attempted to follow the curl as closely as possible. This code results in an error 500 from the server. I suspect it must be something related to the request, because the same server is ok via `curl. Any ideas?
There probably is nothing wrong with your python script.
Differences I've noticed between curl and requests are the following:
obviously, User-Agent headers are different — curl/7.47.0 vs. python-requests/2.22.0
multipart boundary format in Content-Type header is different — ------------------------6debaa3504bbc177 in curl vs. c1e9f4f617de4d0dbdb48fcc5aab67e0 in requests
therefore Content-Length value will almost certainly be different
multipart/form-data format in body is slightly different — curl adds an extra line (Content-Type: text/plain) before file contents
So depending on your file format, server may not be able to parse requests HTTP request format.
I think the best solution for you now is to compare raw HTTP requests from curl and requests and find what differences are significant.
For example:
Open terminal
Launch netcat with nc -l -p 1234 command. This will listen to HTTP requests on localhost on port 1234 and output raw HTTP requests to terminal.
Send your curl request as it is to localhost:1234 in another tab
Execute your python script as it is using URL localhost:1234 in another tab
Compare raw requests from your netcat output
Here's my attempt:
import requests
headers = {
'Accept': 'application/json',
}
files = {
'file': ('temp.trig', open('temp.trig', 'rb')),
}
response = requests.post('http://localhost:8081/demo/upload', headers=headers, files=files)
In case this doesn't work we really need to read more data on the server side, as Ivan Vinogradov explained well.

Can't upload raw string (csv or json) using S3 pre-signed url

Following instructions on this link using Lambda and API Gateway: https://sookocheff.com/post/api/uploading-large-payloads-through-api-gateway/ I have a setup that allows me to get a pre-signed URL and upload files. I've tested using CURL and it has worked.
But when I try to send raw string (csv format or json format) it fails!
Example of what works
curl --request PUT --upload-file Testing.csv "**pre signed upload url**"
Example of what doesn't work
curl --request PUT -H "Content-Type: text/plain" --data "this is raw data" "**pre signed upload url**"
curl --request PUT --data "this is raw data" "**pre signed upload url**"
Am I making the call incorrectly? Should I be switching to POST and what would the call look like then?
It is not becoz of self signed url, it is becoz of content type with the API Gateway set to,
consumes:
- application/json
produces:
- application/json
You add additional content types, it should make it through.
Hope it helps.
So the solution was specifying the content-type during the pre-signed url generation and then the same one in the CURL put command. Figured out thanks to answer here: S3 PUT doesn't work with pre-signed URL in javascript and pointer from #Kannaiyan in the right direction regarding content-types

Using different content-type in Flask Request on GAE

I'm trying yo pass a Content-Type to a flask app (running on GAE), But from python I cannot get the content type header even though I'm passing it
The server-side handler is the following:
#app.route('/api/handlers',methods=['POST'])
def color_list_post():
if(request.headers['Content-Type']=='application/color'):
logging.info('my-format')
elif(request.headers['Content-Type']=='application/x-www-form-urlencoded'):
logging.info('url-encoded')
else:
logging.info('wrong content-type')
return ""
The header passed is:
application/color
this is my request:
curl -H "Content-Type:application/color" -X POST http://localhost:8080/api/handlers
and this the error I get:
KeyError: 'CONTENT_TYPE'

Curl -d : Contents of the file must be URL Encoded

I have an app running in Python using Flask.
The endpoint of the API looks like this:
#app.route('/postIt', methods =['POST'])
def postReview():
#print flask.request
if flask.request.method == 'POST':
posts = flask.request.get_json()
print posts
return str(posts)
I am trying to send it request using CURL:
curl http://127.0.0.1:5000/postIt -d #post.json -H "Content-Type: application/json"
where post.json looks like this:
{"post1":"3", "post2": "2", "post3":"3", "post4":"4" "post5": "5"}
This is not working well. Output on server side prints nothing implying that it is unable to get the json file that I am sending.
I looked into -d flag of CURL and found this thing:
The contents of the file must already be URL-encoded.
So I am guessing there must be encoding issues with my post.json file. I don't exactly know which encoding should be used here. Please help!!
When I tried your code, I got 400 Bad Request:
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
Then I found that your json file is actually not valid. There is a missing , between post4 and post5. After fixing it, I got the correct response:
{u'post5': u'5', u'post4': u'4', u'post3': u'3', u'post2': u'2', u'post1': u'3'}%

Categories