Sending multipart request using aiohttp with files - python

I have code that uses python requests to send a multipart request to a server.
the call looks like so:
files = { 'complex_data': (tuple with binary data)}
data = { 'simple_dict': value }
self.session.request(method='POST',full_uri, timeout=self.timeout, data=data,files=files)
On the server side, it's being read using request.multipart.file and request.multipart.field (for the 'files' and 'data' part respectively).
I want to update the client call to aiohttp, but I'm having difficulties getting the same effect. I cannot change the server handling.
I have tried:
with aiohttp.MultipartWriter("form-data") as mpwriter:
mpwriter.append_form(files)
mpwriter.append_json(data)
response = await self.session.request(method='POST',
full_uri,
timeout=self.timeout,
data=mpwriter)
This code doesn't seem to pass the data to the server correctly.
When trying to use append only (instead of append_form), I receive a parsing error on the client side.
I know there's some 'payload' option, but I couldn't find any information on how to use it correctly.

Related

Receiving 'BadRequestKeyError' when attempting to GET data from endpoint url

I was trying to implement an API using Python, and flask to help myself learn and practice REST.
The idea was to receive a HTTP POST with data that looks like as such:
{"startDate":"2015-07-01","endDate":2015-07-08","within":{"value":9000,"units":miles}} and send some of the data to a NASA API(endpoint).
I was able to create a POST method , and I am able to receive the data (both in POSTMAN and in the browser). Here is the relevant code :
#neows.route('/UserInput',methods=['GET','POST'])
def UserInput():
startDate = request.args.get('startDate')
endDate = request.args.get('endDate')
#print (type(startDate))
#print (type(endDate))
getAsteroids(startDate,endDate)
return jsonify(request.args)
But when I extract some data from the POST above to send to a NASA API (GET), I am receiving this error:
werkzeug.exceptions.BadRequestKeyError
Here is the url I am trying to hit : (https://api.nasa.gov/neo/rest/v1/feed?start_date=START_DATE&end_date=END_DATE&api_key=API_KEY)
I am able to hit the url both on POSTMAN and browser, outside of my code.
The relevant piece of code with the error is posted below and the line that seems to be throwing the error is in Italics (marked with *).
def getAsteroids(startDate,endDate):
API_KEY='xxx'
print (startDate)
print (endDate)
*result=request.args["https://api.nasa.gov/neo/rest/v1/feed?
start_date="+startDate+"&end_date="+endDate+"&api_key="+API_KEY+""]*
I would really appreciate if some one could help me understand and resolve this issue.
If you want to do a request against the NASA's API you can use requests module. (Or any other module to send HTTP requests)
import requests
# ...
def getAsteroids(startDate, endDate):
API_KEY='xxx'
payload = {'start_date': startDate, 'end_date': endDate, 'api_key': API_KEY}
result = requests.get('https://api.nasa.gov/neo/rest/v1/feed', params=payload)
request.args is something different used to get the parameters of the incoming request.

Flask returns a response without even checking files

I am trying to understand more precisely how Http connections work with Flask, so I tried writing a very simple app and another simple connection with requests:
app = Flask('file-streamer')
#app.route("/uploadDumb", methods=["POST"])
def upload_dumb():
print("Hello")
return Response(status=200)
Then I am sending a big file (1.5 GB) to the endpoint with requests :
url = "http://localhost:5000/uploadDumb"
values = {"file": open(file, "rb")}
r = requests.post(url=url, data={}, files=values)
I expected Flask to wait for the whole file to be sent, even if the file is useless. However this is not what's happening, instead Flask returns a 200 response very quickly, which causes a BrokenPipeError with my second script.
Can someone explain to me what is happening here ?
I suppose that happens because Flask body-parsing is lazy (which is a good thing, actually). So when request comes, Flask only reads headers until body is accessed somehow (through request.data, request.files, request.json(), etc.). So to trigger a full body (and file) upload, try accessing request body to make Flask parse a file, like:
_ = request.data

How do I properly format data in a Python POST request to Streamlabs API?

I'm trying to connect our application to Streamlabs' API so we can post donation alerts. To do that, I need to get an access token for the user whose channel we want to alert on. It's a normal OAuth2 thing. We hit the /authorize endpoint and get a code back, which we're then supposed to able to use to get an access token from the /token endpoint (https://dev.streamlabs.com/v1.0/reference#token-1).
But when we send the POST request for the token, we get an error saying the request is missing the "grant_type" parameter.
We're using the normal requests library. I've tried changing the format of the request to requests.post. I've tried altering the data by wrapping it in urlencode and json.dumps. Still no luck.
streamlabs_client_id = config('STREAMLABS_CLIENT_ID')
streamlabs_client_secret = config('STREAMLABS_CLIENT_SECRET')
streamlabs_redirect_uri = config('STREAMLABS_REDIRECT_URI')
grant_type = 'authorization_code'
querydict = {
"grant_type":"authorization_code",
"client_id":streamlabs_client_id,
"client_secret":streamlabs_client_secret,
"redirect_uri":streamlabs_redirect_uri,
"code":code
}
url = "https://streamlabs.com/api/v1.0/token"
streamlabs_response = requests.request("POST", url, data=querydict)
This is the json I get back every time:
{"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"grant_type\" parameter."}
Any ideas what I'm doing wrong with the data?

Amazon Kinesis Video GetMedia/PutMedia

I used python 3.6 and I want to post video stream to aws kinesis with API.
I used python aws client to create stream and GetDataEndPoint but when I want to post my data with my custom request (PutMedia doesn't include in python client actually), I get an error Unable to determine service/operation name to be authorized.
I've follow the api doc of aws kinesis video media PutMedia and GetMedia.
So I start by getting endpoint with GetDataEndPoint with client method:
response = client.get_data_endpoint( # aws client method
StreamName=STREAM_NAME,
APIName='PUT_MEDIA'
)
end_point = response['DataEndpoint'] # https://s-EXAMPLE.kinesisvideo.eu-west-1.amazonaws.com
and I post my data at this url:
headers = {
"x-amzn-stream-arn": STREAM_ARN,
"x-amzn-fragment-timecode-type": "ABSOLUTE",
"x-amzn-producer-start-timestamp": start_tmstp
}
# Sign header...
response = requests.post(end_point, data=data, headers=headers) # 403 - Unable to determine service/operation name to be authorized
So I don't understand why I get this error... I've found this troubleshooting on aws doc. But they say we must specify ApiName parameter. What I do...
This error might occur if the endpoint is not properly specified. When you are getting the endpoint, be sure to include the following parameter in the GetDataEndpoint call, depending on the API to be called:
I'm also wondering if the GetMedia method is actually implemented in client as they say here because when I debug this method, client don't call GetDataEndPoint and so make request at https://kinesisvideo.region.amazonaws.com insteed of https://ID_EXAMPLE.kinesisvideo.region.amazonaws.com. So method get error Unable to determine service/operation name to be authorized as explained in troubleshooting
The error you're getting is because you're probably providing the endpoint without the "action" that in your case would be putMedia.
Try to append /putMedia to your endpoint and don't forget to specify the "content-type": "application/json" header.
Btw you have also to generate the v4 signatures for your request. You can use a lib or follow this python guide to do it.

Push notifications on a Python server with App engnine

I'm implementing a push notifications mechanism for my android app.
Right now I'm trying to run a small test just to see that I manage to send push notifications from a python server via http to GCM, and to recieve it successfuly in an android client.
Client code is exactly as in google's tutorial:
http://developer.android.com/google/gcm/client.html
There's a main activity called DemoActivity which is responsible for registering or retrieving an already existed a registration id, and two classes GcmIntentService and GcmBroadcastReceiver responsible for handling the messags from the GCM server to the app.
Of course, I've set the SENDER_ID correctly, and I do manage to get a registration ID for my client app.
Now for the server:
In the server I always recieve the following error:
HTTP Error 401: Unauthorized
This is my server code:
url = "https://android.googleapis.com/gcm/send"
headers = { 'Content-Type' : 'application/json', 'Authorization': 'key=' + SERVER_API_KEY }
values = { 'registration_ids': [CLIENT_REGID]
, 'data': {'test': 'test} }
data = urllib.urlencode(values)
req = urllib2.Request(url, json.loads(values), headers)
response = urllib2.urlopen(req)
the_page = response.read()
self.response.out.write(the_page)
For security reasons I omitted the server api key and the client registration id (they're hard-coded), but I double and triple checked them and they're correct. Also, I made sure the server API key was formed correctly (Credentials -> Create new key -> Server key) and also made sure "Any IP allowed".
All solutions I found on the internet was related to a mistake in the server api or something like that but I already checked that.
Thanks for helpers!
edit:
added 'key=' in the header, but now I recieve Bad request error (HTTP code 400)
another edit:
changes the values object abit and sent it using json.loads, but not I have this error in the client (means it finally recieves a notification from server!!):
Unable to instantiate receiver GcmBroadcastReceiver
Any ideas? I copied the sample project from google as is, so I don't have any idea what's wrong here.
The auth header should be (note the key= part):
Authorization:key=<your_key_here>
so you should set headers like this:
headers = { 'Content-Type' : 'application/json', 'Authorization': 'key='+SERVER_API_KEY }
I believe the issue is that you're sending urlencoded payload, when telling the server to expect json. Try changing data to a json object, as a string: data ="{ 'registration_ids':...}"

Categories