Create mime/multipart request containing multiple HTTP requests - python

I am following this tutorial for batching http requests with ASP.NET 4.5. I have the sample working, and now I need to write a client application in Python.
This code creates, and sends a batch request to the web api:
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();
//Create a request to query for customers
HttpRequestMessage queryCustomersRequest = new HttpRequestMessage(HttpMethod.Get, serviceUrl + "/Customers");
//Create a message to add a customer
HttpRequestMessage addCustomerRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "/Customers");
addCustomerRequest.Content = new ObjectContent<Customer>(addedCustomer, formatter);
//Create a message to update a customer
HttpRequestMessage updateCustomerRequest = new HttpRequestMessage(HttpMethod.Put, string.Format(serviceUrl + "/Customers/{0}", updatedCustomer.Id));
updateCustomerRequest.Content = new ObjectContent<Customer>(updatedCustomer, formatter);
//Create a message to remove a customer.
HttpRequestMessage removeCustomerRequest = new HttpRequestMessage(HttpMethod.Delete, string.Format(serviceUrl + "/Customers/{0}", removedCustomer.Id));
//Create the different parts of the multipart content
HttpMessageContent queryContent = new HttpMessageContent(queryCustomersRequest);
HttpMessageContent addCustomerContent = new HttpMessageContent(addCustomerRequest);
HttpMessageContent updateCustomerContent = new HttpMessageContent(updateCustomerRequest);
HttpMessageContent removeCustomerContent = new HttpMessageContent(removeCustomerRequest);
//Create the multipart/mixed message content
MultipartContent content = new MultipartContent("mixed", "batch_" + Guid.NewGuid().ToString());
content.Add(queryContent);
content.Add(addCustomerContent);
content.Add(updateCustomerContent);
content.Add(removeCustomerContent);
//Create the request to the batch service
HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "/batch");
//Associate the content with the message
batchRequest.Content = content;
//Send the message
HttpResponseMessage batchResponse = await client.SendAsync(batchRequest);
This is my attempt in Python but it doesn't work:
def build_request( url, type, headers, data = {}):
#Build appropriate request
if type == "get":
request = Request('GET', url, headers=headers)
elif type == "post":
request = Request('POST', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
elif type == "delete":
request = Request('DELETE', url, headers = {'Content-Type':'application/json'})
elif type == "put":
request = Request('PUT', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
elif type == "patch":
request = Request('PATCH', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
prepared_request = request.prepare()
return prepared_request
#Get customers
get_customers = build_request( url + "/Customers", "get", headers)
#Add a customer
add_customer = build_request( url + "/Customers", "post", data=added_customer, headers=headers)
#update a customer
update_customer = build_request( url + "/Customers/{0}".format(updated_customer["Id"]), "put", data=updated_customer, headers=headers)
#Remove a customer
remove_customer = build_request( url + "/Customers/{0}".format(removed_customer["Id"]), "delete", headers=headers)
request_list = [get_customers,add_customer,update_customer, remove_customer]
batch_request = requests.Request('POST',url + "/batch", data=request_list)
s = Session()
batch_request.prepare()
resp = s.send(batch_request)
The request should look like this:
POST http://localhost:12345/api/batch HTTP/1.1
Content-Type: multipart/mixed; boundary="batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e"
Host: localhost:12345
Content-Length: 857
Expect: 100-continue
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
GET /api/WebCustomers HTTP/1.1
Host: localhost:13245
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
POST /api/WebCustomers HTTP/1.1
Host: localhost:13245
Content-Type: application/json; charset=utf-8
{"Id":129,"Name":"Name4752cbf0-e365-43c3-aa8d-1bbc8429dbf8"}
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
PUT /api/WebCustomers/1 HTTP/1.1
Host: localhost:13245
Content-Type: application/json; charset=utf-8
{"Id":1,"Name":"Peter"}
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
DELETE /api/WebCustomers/2 HTTP/1.1
Host: localhost:13245
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e--

I've managed to translate the first part of the C# sample into Python using the following code:
class BatchRequest:
'Class for sending several HTTP requests in a single request'
def __init__(self, url, uuid):
self.batch_url = batch_url
self.uuid = uuid
self.data = ""
self.headers = {"Content-Type" : "multipart/mixed; boundary=\"batch_{0}\"".format(uuid)}
#Build sub-request depending on Method and Data supplied
def add_request(self, method, url, data={}):
if method == "GET":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\n\r\n\r\n".format(uuid, method, url)
#If no data, have alternative option
elif method == "POST" or method == "PUT":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\nContent-Type: application/json; charset=utf-8\r\n\r\n{3}\r\n".format(uuid, method, url, json.dumps(data))
elif method == "DELETE":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\n\r\n\r\n".format(uuid, method, url)
def execute_request(self):
#Add the "closing" boundary
self.data += "--batch_{0}--\r\n".format(uuid)
result = requests.post(self.batch_url, data=self.data, headers=self.headers)
return result
if __name__ == '__main__':
url = "http://localhost:65200/api"
batch_url = "{0}/batch".format(url)
uuid = uuid.uuid4()
br = BatchRequest("http://localhost:65200/api", uuid)
#Get customers
br.add_request("GET", "/api/Customers")
#Create a new customer
new_customer = {"Id" : 10, "Name" : "Name 10"};
br.add_request("POST", "/api/Customers", new_customer)
#Update the name of the first customer in the list
update_customer = customer_list[0]
update_customer["Name"] = "Peter"
br.add_request("PUT", "/api/Customers/{0}".format(update_customer["Id"]), update_customer)
#Remove the second customer in the list
br.add_request("DELETE", "/api/Customers/{0}".format(customer_list[1]["Id"]))
result = br.execute_request()
All that remains is to figure out how to parse the response(s) from the server.

Related

Python Falcon - Post calls are being ignored

I'm trying to set up a simple reverse proxy with Falcon in Python.
I have:
import falcon
import requests
class ReverseProxyResource:
def on_get(self, req, resp, text=None):
print("GET")
if(text):
destination = "[destination_url]/" + text
else:
destination = "[destination_url]"
result = requests.get(destination)
resp.body = result.text
resp.status = result.status_code
def on_post(self, req, resp, text=None):
print("POST")
if(text):
destination = "[destination_url]/" + text
else:
destination = "[destination_url]"
result = requests.post(destination, data=req.bounded_stream.read())
resp.body = result.text
resp.status = result.status_code
proxy_api = application = falcon.API()
proxy_api.req_options.auto_parse_form_urlencoded = True
proxy_api.add_route('/{text}', ReverseProxyResource())
proxy_api.add_route('/', ReverseProxyResource())
Get requests to the proxy are returned correctly.
However, Post requests are only returned a 404 error from the api. The "POST" print statement is not shown, indicating on_post isn't called at all. (The post requests only included Header Content-Type: application/json and a simple JSON body, which work correctly when called directly against the destination url)
EDIT: Interestingly enough, if I change GET call in postman to POST (ie: no body, headers, or anything else added) on_post() is called when I hit the endpoint. So it seems like an issue where post requests that contain a body are being automtically 404'ed without calling on_post()
Try adding user agent and content type before making the post call
headers = {"Content-Type": "text/plain", "User-Agent": "PostmanRuntime/7.30.0"}
result = requests.post(url = destination, data=req.bounded_stream.read(), headers=headers)
below code works for me
import falcon
import requests
class ReverseProxyResource:
def on_get(self, req, resp, text=None):
print("GET")
if(text):
destination = "https://cat-fact.herokuapp.com/" + text
else:
destination = "https://cat-fact.herokuapp.com/facts/"
result = requests.get(destination)
resp.body = result.text
resp.status = result.status_code
def on_post(self, req, resp, text=None):
print("POST")
if(text):
destination = "https://dummy.restapiexample.com/api/v1/"+ text
else:
destination = "https://dummy.restapiexample.com/api/v1/create"
headers = {"Content-Type": "text/plain", "User-Agent": "PostmanRuntime/7.30.0"}
result = requests.post(url = destination, data=req.bounded_stream.read(), headers=headers)
resp.text = result.text
resp.status = result.status_code
proxy_api = application = falcon.App()
proxy_api.req_options.auto_parse_form_urlencoded = True
proxy_api.add_route('/{text}', ReverseProxyResource())
proxy_api.add_route('/', ReverseProxyResource())

Requests: Error sending payload in python post request

The code behaving strangely that I am unable to understand what's going on..
The code that works fine:
link = "https://api.luminati.io/dca/trigger_immediate?collector=XXXxxxXXXxxxXXXX"
head = {"Authorization": "Bearer xxXXxxXXxx" ,"Content-Type": "application/json"}
data = '{"url":"https://www.practo.com/pune/doctor/XXXXXXxXXXXX"}'
res = requests.post(link, headers = head, data = data)
print("Status: "+str(res.status_code), "Message: "+res.text)
Output:
Status: 202 Message: {"response_id":"z7627t1617552745375r14623bt37oo"}
But I want to load "url":"https://www.practo.com/pune/doctor/XXXXXXxXXXXX" this thing dynamically.
url = "https://www.practo.com/pune/doctor/XXXXXXxXXXXX"
link = "https://api.luminati.io/dca/trigger_immediate?collector=XXXxxxXXXxxxXXXX"
head = {"Authorization": "Bearer xxXXxxXXxx" ,"Content-Type": "application/json"}
data = {"url":url}
res = requests.post(link, headers = head, data = data)
print("Status: "+str(res.status_code), "Message: "+res.text)
Output:
Status: 500 Message: 'Unexpected token u in JSON at position 7'
To load data dynamically try using %s string feature, like that:
url = "https://www.practo.com/pune/doctor/XXXXXXxXXXXX"
data = '{"url":"%s"}' % url
or you can convert dictionary entirely to str, like:
import json
data = {"url":link}
res = requests.post(link, headers=head, data=json.dumps(data))
by the way, you can pass body not like data, but like json, here's documents:
:param json: (optional) json data to send in the body of the :class:Request. So your request will look like:
data = {"url":link}
res = requests.post(link, headers=head, json=data)
any_dynamic_variable='XXXXXXxXXXXX'#You can make your websit or part of the url to be dynamic
url = f'https://www.practo.com/pune/doctor/{any_dynamic}'
headers = {"Authorization": "Bearer xxXXxxXXxx" ,"Content-Type": "application/json"}
payload={
'your_key':'your_value',
'currancy':'CA',#<==== This is an example
}
r = requests.post(url,headers=headres,data=payload}
print(r.status_code)#check your status to be able to find the error if you have any
print(r.text)

I want to send email to user from API response in Django?

[data image][1]I want to send email to user after this function calling so in first function i m creating customer on razor pay and after fetching customer id i m passing to other function and on.
so in this function get_virtual_account() i am getting all the response from API provided by razor-pay and i need to send that response to user who created the account so how can i do that i am not able to send this response in email.
def create_razor_customer(data):
logger.info("Inside create_razor_customer")
headers = {'Content-Type': 'application/json',}
data=json.dumps(data)
response = requests.post(settings.API_RAZORPAY+'/customers', headers=headers, data=data, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
logger.info(json.loads(response.content))
json_response = response.json()
customer_id = json_response['id']
logger.info(customer_id)
get_razor_customer(customer_id)
return response
def get_razor_customer(customer_id):
logger.info("Inside get_razor_customer")
headers = {'Content-Type': 'application/json',}
response = requests.get(settings.API_RAZORPAY+'/customers/'+customer_id, headers=headers, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
logger.info(json.loads(response.content))
create_razor_virtual_account(customer_id)
return response
def create_razor_virtual_account(customer_id):
logger.info("Inside create_razor_virtual_account")
headers = {'Content-Type': 'application/json',}
data = {"receivers": {"types": ["bank_account"]},"description": "razorpay","customer_id": customer_id,"close_by": 1761615838,"notes": {"reference_key": "reference_value"}}
data=json.dumps(data)
response = requests.post(settings.API_RAZORPAY+'/virtual_accounts', headers=headers, data=data, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
json_response = response.json()
virtual_id = json_response['id']
logger.info(virtual_id)
logger.info(json_response)
return response
def get_virtual_account(virtual_id):
logger.info("Inside get_virtual_account")
logger.info(virtual_id)
headers = {'Content-Type': 'application/json',}
response = requests.get(settings.API_RAZORPAY+'/virtual_accounts/'+virtual_id,headers=headers, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
json_response = response.json()
logger.info(json_response)
send_account_details()
return response
def send_account_details():
logger.info('Inside send_account_details')
send_mail('Account Details', 'Details for Razorpay account', settings.EMAIL_HOST_USER, ['abhishek#byond.travel',])
logger.info("sent")
return "sent"
[1]: https://i.stack.imgur.com/proIH.png
Suppose your response JSON is
res_data={"duration":1201,"number":6,"result":"FAILURE","url":"http://localhost:8080/job/git_checkout/6/"}
Then you need to pass this into send_account_details function.
def send_account_details(res_data):
import json
logger.info('Inside send_account_details')
body = "JSON Response is : " + json.dumps(res_data) + "\n\n" + "Details for Razorpay account"
send_mail('Account Details', body, settings.EMAIL_HOST_USER, ['abhishek#byond.travel',])
logger.info("sent")
return "sent"
Using json.dumps(res_data) you can add your JSON to body of email.

Why there is different response in python and ruby mechanize for the same HTTP GET request?

Why there is different response in python and ruby mechanize for the same HTTP GET request?
Python:
def send(cls, url, values = None, method = 'GET', header={}):
print(url)
c = cls()
data = None
context = ssl._create_unverified_context()
if values is not None:
# data = urllib.parse.urlencode(values)
# data = data.encode('utf-8') # data should be bytes
data = json.dumps(values)
data = data.encode('utf-8')
if data is None:
c.request = urllib.request.Request(url)
else:
c.request = urllib.request.Request(url, data)
c.request.add_header('Content-Type', 'application/json')
for key, value in header.items():
c.request.add_header(key,value)
c.request.method = method
# print(c.request.get_header('Content-Type'))
try:
c.response = urllib.request.urlopen(c.request, context=context)
except urllib.error.HTTPError as e:
c.response = e
# print(e.hdrs)
return c
resp = HTTPSender.send("https://10.5.110.143/__api__/logon/704383e332d46036369a510e2673fe94?method=144", method="GET")
print(resp.body)
Output :
{
"message": "The method is not allowed for the requested URL."
}
Ruby mechanize:
#agent=Mechanize.new
$resp = .get("https://10.5.110.143/__api__/logon/704383e332d46036369a510e2673fe94?method=144")
puts $resp.content
Output:
405 => Net::HTTPMethodNotAllowed for https://10.5.110.143/api/logon/9c7ca3f5
34ea1c8a5bfe64d7cf08797a?method=144 -- unhandled response
Is it something expected?

Http post successes with poster, but fails in python

In the code below, I'm trying to create a repository with http post, but I always get 400 bad request, when I send the http post with poster, I got 201 created, what's wrong with this code?
token = raw_input('Access Token: ')
url = 'https://api.github.com/user/repos?access_token=' + token
values = {"name":"newnewnewnew"}
data = urllib.urlencode(values)
req = urllib2.Request(url,data)
response = urllib2.urlopen(req)
the_page = response.read();
print the_page
Poster:
According to the GitHub API v3 documentation, for POST request, the parameters should be encoded with json and the content-type should be application/json:
import json
....
token = raw_input('Access Token: ')
url = 'https://api.github.com/user/repos?access_token=' + token
values = {"name": "newnewnewnew"}
data = json.dumps(values) # <---
req = urllib2.Request(url, data, headers={'Content-Type': 'application/json'}) # <---
response = urllib2.urlopen(req)
the_page = response.read()
print the_page

Categories

Resources