Python - How to manipulate FastAPI request headers to be mutable? - python-3.x

I am trying to change my request headers in my api code. Its immutable right now oob with fastapi starlette. how can i change it so my request headers are mutable? i would like to add, remove, and delete request headers. i tried to instantiate a new request and directly modify the request using
request.headers["authorization"] = "XXXXXX"
but I get the following error
TypeError: ‘Headers’ object does not support item assignment
Thanks!

I'm assuming you want to do something with the header in a middleware. Because FastAPI is Starlette underneath, Starlette has a data structure where the headers can be modified. You can instantiate MutableHeaders with the original header values, modify it, and then set request._headers to the new mutable one. Here is an example below:
from starlette.datastructures import MutableHeaders
from fastapi import Request
#router.get("/test")
def test(request: Request):
new_header = MutableHeaders(request._headers)
new_header["xxxxx"]="XXXXX"
request._headers = new_header
print(request.headers)
return {}
Now you should see "xxxxx" is in the print output of your request.headers object:
MutableHeaders({'host': '127.0.0.1:8001', 'user-agent': 'insomnia/2021.5.3', 'content-type': 'application/json', 'authorization': '', 'accept': '*/*', 'content-length': '633', 'xxxxx': 'XXXXX'})

Related

REST API Post method Recursive run returning Different data-probably data with error in Azure Functions

I am using REST API Post method and deployed to Azure functions .The First Run[immediate run post changes and deployment to azure] is success and subsequent runs are showing error in data and counts are not same as the first Run.Following is the code snippet that I am using.
` def __init__(self):
url = "https://login.microsoftonline.com/abcdefgh12345/oauth2/v2.0/token"
payload = 'grant_type=client_credentials&scope=api%AB1234567890CDE%.default'
headers = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + "ABCDEFG",
'Cookie': 'XYZMANOP'
}
response = requests.request("POST", url, headers=headers, data=payload)
self.auth = response.json()`
Then I am using self.auth in other methods
Why is this happening and what is the solution???PS:When I try the code in local it works like a charm.
I tried extracting the access_token and added it in subsequent functions. But the result is also the same. Also I changed the schedule since access_token is valid only for 3600s and rescheduled to execute post 2 hours.But its returning same result

AWS API Gateway 'Access-Control-Allow-Origin' header is not present

So, like many before me, I'm also facing the CORS error with AWS API gateway+Lambda(python) for a POST request.
Let me explain the Homeworks I did.
Followed the links and got a basic idea of how CORS works.
Tried enabling lambda proxy integration and tried without it as well.
During the manual configuration attempt I added the "Access-Control-Allow-Origin":'*' manually in API gateway method configurations.
At all times my lambda function is set to return the headers like below:
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
}
Postman is working fine as it worked for most of the people who had issues.
When I check the network traffic in chrome, I get the 'Access-Control-Allow-Origin': '*' as part of the header for OPTIONS. But when POST request has none of these headers I have added in the lambda.
The destination page is hosted in my local and AWS Amplify and both has the same issue.
Few images for reference.
Looking forward to all of your inputs.
Edit:
Adding my lambda code as requested:
import json
import urllib.parse
import boto3
import configparser
import os
import datetime
import json
print('Loading function')
# some more code here...
def lambda_handler(event, context):
logfilename = log(json.dumps(event, indent=2), "Debug")
response = {
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
},
"statusCode": 200,
"body": "{\"result\": \"Success.\"}"
}
return response
You need to add "Secure; SameSite=None" to the cookie you send with the lambda response and add withCredentials: true to your axios request.
Important! The cookie will not be accessible with JS inside you app (as it is httpOnly one). But it will be added to external HTTP requests (execute with axios withCredentials: true)

Error calling CF API login one time passcode

I am working with the CF API RESTful services. Trying to get an access token from cloud foundry's UAA API using https://login..../oauth/token web method.
I have verified that headers & body content is correct, but calling the api always returns a 400 error code with message missing grant type.
I have implemented this call in Objective-C, Swift & now Python. All tests return the same result. Here is my code example in Python:
import json
import requests
import urllib
params = {"grant_type": "password",
"passcode": "xxx"
}
url = "https://login.system.aws-usw02-pr.ice.predix.io/oauth/token"
headers = {"Authorization": "Basic Y2Y6", "Content-Type": "application/json", "Accept": "application/x-www-form-urlencoded"}
encodeParams = urllib.parse.urlencode(params)
response = requests.post(url, headers=headers, data=encodeParams)
rjson = response.json()
print(rjson)
Each time I run this, I get the response
error invalid request, Missing grant type
Any help would be greatly appreciated.
Your code mostly worked for me, although I used a different UAA server.
I had to make only one change. You had the Accept and Content-Type headers flipped around. Accept should be application/json because that's the format you want back, and Content-Type should be application/x-www-form-urlencoded because that's the format you are sending.
See the API Docs for reference.
import json
import requests
import urllib
import getpass
UAA_SERVER = "https://login.run.pivotal.io"
print("go to {}/passcode".format(UAA_SERVER))
params = {
"grant_type": "password",
"passcode": getpass.getpass(),
}
url = "https://login.run.pivotal.io/oauth/token"
headers = {
"Authorization": "Basic Y2Y6",
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
}
encodeParams = urllib.parse.urlencode(params)
response = requests.post(url, headers=headers, data=encodeParams)
rjson = response.json()
print(json.dumps(rjson, indent=4, sort_keys=True))
I made a couple other minor changes, but they should affect the functionality.
Use getpass.getpass() to load the passcode.
Set the target server as a variable.
Pretty print the JSON response.
The only other thing to note, is that the OAuth2 client you use must be allowed to use the password grant type. It looks like you're using the same client that the cf cli uses, so if your UAA server is part of a standard Cloud Foundry install that is likely to be true, but if it still doesn't work for you then you may need to talk with an administrator and make sure the client is set up to allow this.

Create variable string for request API payload

I have a list of URLs that all refer to images. I want to loop through the list and call a face recognition API that accepts these URLs. To call the API, I need to provide the payload dictionary. However, the example code from the API requires the following form for the payload dictionary:
payload = "{\"url\":\"https://inferdo.com/img/face-3.jpg\",\"accuracy_boost\":3}"
The URL in this example payload dictionary would look like this in my list:
list_of_urls = ["https://inferdo.com/img/face-3.jpg", ...]
How can I insert the entries of my list into the payload dictionary with a for loop?
I tried to use a "regular" payload dictionary, but it did not work:
for url_path in list_of_urls:
payload = {'url' : url_path,'accuracy_boost':3}
I went to the API documentation and found you need to send the payload as JSON. Something like this will do the job:
import requests
import json
endpoints = {
'face': 'https://face-detection6.p.rapidapi.com/img/face'
'face_age_gender': 'https://face-detection6.p.rapidapi.com/img/face-age-gender'
}
urls = [
'https://inferdo.com/img/face-3.jpg'
]
headers = {
'x-rapidapi-host': 'face-detection6.p.rapidapi.com',
'x-rapidapi-key': 'YOUR-API-KEY',
'content-type': 'application/json',
'accept': 'application/json'
}
for url in urls:
payload = {
'url': url,
'accuracy_boost': 3
}
r = requests.post(
endpoints.get('face'), # or endpoint.get('face_age_gender')
data=json.dumps(payload),
headers=headers
)
if r.ok:
# do something with r.content or r.json()
I hope it helps.

KeyError when trying to send body to API via function parameter

Attempting to send a POST request to an API via a python function, and I'm unable to iterate through a list of strings and pass strings into the function.
Successfully tested this out in Postman (the request sends a string to the API in the form of "raw Body" as shown in Postman). Copied the code over from Postman to Python, and verified that also works. The problem is that if I change the static string to the function's parameter, I get a KeyError, however if I simply replace the parameter (which is a value, not a key), to a string, then the keyerror goes away.
This works...
payload = "{\"ids\":[\"cb5f9a97c0e749ab67409e78b4fcb11d\"]}" works
But none of these work (note the error code to the right); especially the first two that are exactly the same as above...
payload = "{\"ids\":[\"{0}\"]}".format(id) #gives KeyError: '"ids"'
payload = "{\"ids\":[{0}]}".format(id) #gives KeyError: '"ids"'
payload = "{'ids':'[{0}]'}".format(id) #gives KeyError: "'ids'"
payload = "{ids:[\"{0}\"]}".format(id) #gives KeyError: 'ids'
I also tried to modify the key ('ids') within the key/value pair, which resulted in NameErrors. Since this deviates from the known working example above, I don't think the below attempts are worth continuing to try...
payload = {ids:"[{0}]".format(id)} #gives NameError: name 'ids' is not defined
payload = {ids:"{0}".format(id)} #gives NameError: name 'ids' is not defined
payload = {ids:id} #gives NameError: name 'ids' is not defined
I even verified that the string produced from the list is in fact a string.
Full (relevant) code below:
def cs_delete(id):
print(id)
url = "https://api.crowdstrike.com/devices/entities/devices-actions/v2"
querystring = {"action_name":"hide_host"}
payload = "{'ids':['{0}']}".format(id)
headers = {
'Content-Type': "application/json",
'Authorization': "Bearer " + cs_auth,
'Accept': "*/*",
'Cache-Control': "no-cache",
'Host': "api.crowdstrike.com",
'Accept-Encoding': "gzip, deflate",
'Content-Length': "83",
'Connection': "keep-alive",
'cache-control': "no-cache"
}
response = requests.request("POST", url, data=payload, headers=headers, params=querystring)
print(response.text)
for host in dfList:
print(host)
cs_delete(host)
And for completeness, dfList shows as:
['2a9cf64988e6464f7d2ba7f305a612f3', '5ba4654e1dbe418f7b6361582e3d8f47', '7c6fc20572c241f664813f48bb36c340', 'ccbaf1ebe52042fc6b8269bf86732676']
double the outer curly bracket to escape it or format() will expect the input value should be applied for outer curly bracket rather than {0}
>>> id = 'cb5f9a97c0e749ab67409e78b4fcb11d'
>>> "{{'ids':[{0}]}}".format(id)
"{'ids':[cb5f9a97c0e749ab67409e78b4fcb11d]}"

Resources