I am trying to replicate the successful POST operation performed using Postman in Python. My code is something like this:
import requests
url = "https://api.appx.com/v1/gen_ticket"
headers = {
'authorizationtoken': "Bearer " + access_token,
'x-api-key': "ddQk4mlTAl5cUj0N7omg4457jXuYlH25kOdvJoeJN3",
'catalogitem': "eb29189cd00576b00dd3cf5951d96197d",
'Content-Type': "application/json",
}
response = requests.request("POST", url, headers=headers)
print(response.text)
but when i run this, i keep getting the following error (This is working properly when using Postman but not in Python code).
{"errorMessage":"RequestId: af75334a-f955-11e8-b2e7-17baf7f53f31 Process exited before completing request"}
Can someone please suggest how to fix this?
One more thing to above is, i am passing a JSON file in Postman body section (how can i use the same in python code as well?)
try change authorizationtoken to Authorization
import requests
url = "https://api.appx.com/v1/gen_ticket"
headers = {
'Authorization': "Bearer " + access_token,
'x-api-key': "ddQk4mlTAl5cUj0N7omg4457jXuYlH25kOdvJoeJN3",
'catalogitem': "eb29189cd00576b00dd3cf5951d96197d",
'Content-Type': "application/json",
}
response = requests.post(url, headers=headers)
print(response.text)
Related
I am sending a PATCH request to my DRF server in Postman and it works perfect
However when I do the same in Python I get:
<Response [405]> http://127.0.0.1:8000/api/title/8174/
b'{"detail":"Method \\"PATCH\\" not allowed."}'
Method Not Allowed
My function that sends data:
ss_token = os.getenv('SS_TOKEN')
headers = {
'Authorization': 'Token ' + ss_token,
}
source = Source.objects.all().first()
url = source.url + str(self.ss_id) + '/'
response = requests.patch(source.url, headers=headers, data={'key':'value'})
print(response, url)
print(response.content)
print(response.reason)
return True
Do I have to send other headers to the API to make the PATCH work?
Ah looks like I made a mistake. Forgot to replace source.url with the new url variable called 'url' variable. Because that add the 'ss_id' at the url' so it becomes 'api/title/ID/' instead of just 'api/title'
url = source.url + str(self.ss_id) + '/'
response = requests.patch(url, headers=headers, data={'key':'value'})
Editing again with more updates:
Trying to troubleshoot python-requests to see if something is wrong with a PUT request, but not sure how to proceed.
Below is a snippet of my code:
def API_request(url=None, headers=None, payload=None, update=False):
r = None
if update and headers and payload:
print "put request to %s with %s of %s" % (url, headers, payload)
r = requests.put(url, headers=headers, data=payload)
if headers and payload and not update:
print "post request to %s with %s of %s" % (url, headers, payload)
r = requests.post(url, headers=headers, data=payload)
print r.status_code
print r.text
When the above sends a POST request to create a record, it works. However, whenever it sends a PUT request, I get a 401 error: "Authentication credentials were not provided." This happens across multiple endpoints.
401
{"detail":"Authentication credentials were not provided."}
If I copy/paste the relevant printed output from the above PUT print function into a direct HTTPie request, it works. The below request results in a successful 200 response and updated record on the server:
http --debug PUT [url] < [file containing payload] Authorization:'Token [token]'
If I hard code a simple script that does nothing more than import python and json and PUT the exact same data to the same url using the same headers (printed form the original statement), it works. The below script results in a successful 200 response and updated record on the server:
import requests, json
url = "[my url"
headers = {'Content-Type': 'application/json', 'Authorization': 'Token [my token]'}
data = {[my data]}
payload = json.dumps(data)
r = requests.put(url, headers=headers, data=payload)
print r.status_code
print r.text
I've sent the information from both scripts to https://requestbin.fullcontact.com/ and they look to be identical.
BIG ISSUE:
After an entire day of debugging I figured out that even the requests that were generating a 401 error were successfully hitting the server and updating the appropriate records. Put simply, this would not be possible without a correct and functional authentication. Given that, why would I be getting a 401 error from the PUT request?
Happy to answer any questions in comments.
The above was sending a follow-up GET request (without the header, so it was failing) after successfully sending the PUT request (which was succeeding). I caught this by logging all requests hitting the server.
I figured out why the follow up GET was being sent and corrected that. I still don't understand why the r.code and r.text response from the successful PUT was never printing and hitting the console. Had I seen that, it would have been much easier to find. However, since the main problem is resolved, I can't spend time troubleshooting that.
I should have been seeing both responses - the success and the fail in the console - problem for another time.
I'm working on Entity Extraction usin an API call to https://dandelion.eu/ . I'm sending text files and automatically i get back a json file as response. It's not the first time i use this service and it worked really good. Now I started to send a new set of text file with the same parameters I always used but i get this: ValueError: too many values to unpack.
Here is my code:
values={"text":" ",
"min_confidence":"0.6",
"include":"types",
"include":"abstract",
"include":"categories"
}
headers = {'X-Target-URI':'https://api.dandelion.eu',
'Host':'api.dandelion.eu',
'Connection': 'keep-alive',
'Server': 'Apache-Coyote/1.1',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
}
for roots, dirs, files in os.walk(spath): #spath is specified
for file in files:
if file.startswith("A0"):
with open(file, "r") as f:
text = f.read()
values["text"]= " ".join(text.split())
#api call
url = "https://api.dandelion.eu/datatxt/nex/v1/"
data = urllib.urlencode(values, "utf-8")
response = requests.request("POST", url, data=data, headers=headers, params=token_api)
content = response.json()
print content
ErrorValue: too many values to unpack
Can somebody help me on this? I always used the same code for other api calls and it worked good. I don't know what is wrong now.
the API returns more than one value.
please refer to API documentation and see what are the return values.
(you did not mentioned what API raised the err in tour question)
This is with respect to google drive API integration where I have a Lambda python event code that gets triggered when I do . This is the push notification implentation of Google drive API.
In order to allow google drive push notification to call us, I have created the associated api gateway endpoint as webhook using their API. Now this lambda do gets triggered when I edit the file so this means that webhook is successful and google calls back the hook.
Google Drive sends HTTP POST messages to the webhook url once you do any change.
The following HTTP headers are returned with empty body to the lambda function:
{
"Content-Type": "application/json; utf-8",
"Content-Length": "5000",
"X-Goog_Channel-ID": "05a349fd-c363-4d8c-9409-8b6f310b7379",
"X-Goog-Channel-Token": "to66728b-21c7-4605-8445-d7a297b9ae7f",
"X-Goog-Channel-Expiration": "Fri, 14 Oct 2016 20:05:58 GMT",
"X-Goog-Resource-ID": "SuIweVX_iBzKmM5PQVMbIDYFrr8",
"X-Goog-Resource-URI": "https://www.googleapis.com/drive/v3/files/1QvVo67IJ3_o5g2tCyxpNA29JHx183-bOOblKMoSAGv4?acknowledgeAbuse=false&alt=json",
"X-Goog-Resource-State": "update",
"X-Goog-Changed": "content,properties",
"X-Goog-Message-Number": "480896"
}
However, the event object of lambda handler is empty. I assume that event object is the HTTP body and in my case the body is empty, so I have added custom mapping template in Integration Request (to retrieve headers) of the API Gateway POST method as the following:
#set($inputRoot = $input.path('$'))
{
"Content-Type" : "$input.params('Content-Type')",
"Content-Length" : "$input.params('Content-Length')",
"X-Goog-Channel-ID" : "$input.params('X-Goog-Channel-ID')",
"X-Goog-Channel-Token" : "$input.params('X-Goog-Channel-Token')",
"X-Goog-Channel-Expiration" : "$input.params('X-Goog-Channel-Expiration')",
"X-Goog-Resource-ID" : "$input.params('X-Goog-Resource-ID')",
"X-Goog-Resource-URI" : "$input.params('X-Goog-Resource-URI')",
"X-Goog-Resource-State" : "$input.params('X-Goog-Resource-State')",
"X-Goog-Changed" : "$input.params('X-Goog-Changed')",
"X-Goog-Message-Number" : "$input.params('X-Goog-Message-Number')",
"body" : $input.json('$')
}
But I am not sure how should I retrieve these headers from event object if at all they are getting received from google drive. Just logging the event object shows it as empty {}. What is the expected mapping if the above is incorrect? Also, event['header-name'] is not even getting executed it seems as when I print
print 'Event header:{}'.format(event['header-name']) I don't even get Event header statement in the logs. So it means the line is not exceuted but the line above that is executed which is simple print statement like print 'Printing results' I am not sure why the code is not getting there or if there is an error.
Could you please guide me how we can retrieve headers using event object in python? And if all my steps above look correct or am I missing something?
I was able to get this working without any problem. I can't tell from your post why your event object is empty. My best guess is that you forgot to add the headers to the "HTTP Request Headers" section of the Method Request.
Here's my Lambda function:
from __future__ import print_function
import json
print('Loading function')
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
print("Content-Type = " + event['Content-Type'])
print("Content-Length = " + event['Content-Length'])
print("X-Goog-Channel-ID = " + event['X-Goog-Channel-ID'])
print("X-Goog-Channel-Token = " + event['X-Goog-Channel-Token'])
print("X-Goog-Channel-Expiration = " + event['X-Goog-Channel-Expiration'])
print("X-Goog-Resource-ID = " + event['X-Goog-Resource-ID'])
print("X-Goog-Resource-URI = " + event['X-Goog-Resource-URI'])
print("X-Goog-Resource-State = " + event['X-Goog-Resource-State'])
print("X-Goog-Changed = " + event['X-Goog-Changed'])
print("X-Goog-Message-Number = " + event['X-Goog-Message-Number'])
print("body = " + event['body'])
return {
'statusCode': '200',
'body': event['body'],
'headers': {
'Content-Type': 'application/json',
'X-Goog-Channel-ID': event['X-Goog-Channel-ID'],
'X-Goog-Channel-Token': event['X-Goog-Channel-Token'],
'X-Goog-Channel-Expiration': event['X-Goog-Channel-Expiration'],
'X-Goog-Resource-ID': event['X-Goog-Resource-ID'],
'X-Goog-Resource-URI': event['X-Goog-Resource-URI'],
'X-Goog-Resource-State': event['X-Goog-Resource-State'],
'X-Goog-Changed': event['X-Goog-Changed'],
'X-Goog-Message-Number': event['X-Goog-Message-Number'],
}
}
My mapping template is the same as yours.
Here's the return value from a test call, using the same input headers from your example:
{
"body": "Test input body content",
"headers": {
"X-Goog-Resource-ID": "SuIweVX_iBzKmM5PQVMbIDYFrr8",
"X-Goog-Channel-ID": "",
"X-Goog-Resource-State": "update",
"X-Goog-Changed": "content,properties",
"X-Goog-Resource-URI": "https://www.googleapis.com/drive/v3/files/1QvVo67IJ3_o5g2tCyxpNA29JHx183-bOOblKMoSAGv4?acknowledgeAbuse=false&alt=json",
"X-Goog-Message-Number": "480896",
"X-Goog-Channel-Token": "to66728b-21c7-4605-8445-d7a297b9ae7f",
"Content-Type": "application/json",
"X-Goog-Channel-Expiration": "Fri, 14 Oct 2016 20:05:58 GMT"
},
"statusCode": "200"
}
I am building both an iOS client and a django backend service. The connection made between the systems is OAUTH2, implemented by the django-oauth2-toolkit.
Although the following command done in curl works (returns an access token):
curl -X POST -d "grant_type=password&username=<user>&password=<password>" http://<clientID>:<clientSecret>#localhost:8000/o/token/
The following Swift snippet, that uses Alamofire, receives "invalid_client", as a response.
let request = "http://\(Authentication.clientId):\(Authentication.clientSecret)#localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password", "username": in_username.text!, "password": in_password.text!]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
URLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then traced the InvalidClientError in the django-oauth2-toolkit source, and found that the exception was raised in the highlighted snippet of the following file:
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
if self.request_validator.client_authentication_required(request):
log.debug('Authenticating client, %r.', request)
print(request) # I included this print message to inspect the request variable in console.
if not self.request_validator.authenticate_client(request):
log.debug('Client authentication failed, %r.', request)
raise errors.InvalidClientError(request=request) # RAISED!
I included the print(request) line to inspect the differences between the request made by curl and by Alamofire. The major difference was that the curl version included an authorization key:
'Authorization': 'Basic Z3ZFSjVXejloUGgybUJmdDNRaGhXZnlhNHpETG5KY3V6djJldWMwcjpSbVNPMkpwRFQ4bHp1UVFDYXN3T3dvVFkzRTBia01YWWxHVHNMcG5JUGZCUHFjbHJSZE5EOXQzd3RCS2xwR09MNWs1bEE4S2hmRUkydEhvWmx3ZVRKZkFXUDM4OERZa1NTZ0RvS0p3WjUyejRSQ29WRkZBS01RS1lydEpsTWNXag=='
and the Alamofire request didn't.
I highly suspect this is the culprit, but I really don't know else to do though from here on. I would really appreciate any wisdom.
Found the answer!
Was reading through a RFC document on the HTTP protocol, when this section caught my eye.
https://www.rfc-editor.org/rfc/rfc1945#section-11.1
Specifically,
To receive authorization, the client sends the user-ID and password,
separated by a single colon (":") character, within a base64 [5]
encoded string in the credentials.
It seems that Alamofire does not encode in 64 bits the clientId and clientSecret, as expected. Obviously, curl does this automatically. So I did the following:
First encode:
static let clientData: NSData = "\(clientId):\(clientSecret)".dataUsingEncoding(NSUTF8StringEncoding)!
static let client64String = clientData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.init(rawValue: 0))
Then set the request header using the resulting value:
let request = "http://localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password",
"username": in_username.text!,
"password": in_password.text!,
]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
// SOLUTION!
URLRequest.setValue("Basic \(Authentication.client64String)", forHTTPHeaderField: "Authorization")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then received the expected token as a response.