Can't access or print any request data with FastAPI - python

I have a simple FastAPI endpoint, where I want to receive a string value. In this case, I tried it with a JSON body, but basically it doesn't need to be JSON. I really need only a simple string to separate the requests from each other. Unfortunately, I can't access any of the request parameters with a GET method. I also tried POST method instead, but I get an error:
request:
url = "http://127.0.0.1:5000/ping/"
payload=json.dumps({"key":"test"})
headers = {
"Content-Type": "application/json"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.text)
api:
#app.get("/ping/{key}")
async def get_trigger(key: Request):
key = key.json()
test = json.loads(key)
print(test)
test2 = await key.json()
print(key)
print(test2)
return
I can't print anything with post or put:
#app.post("/ping/{key}")
async def get_trigger(key: Request):
...
or
#app.put("/ping/{key}")
async def get_trigger(key: Request):
I get a 405 Method not allowed error.
How can I get this fixed?

The 405 Method Not Allowed status code indicates that "the server knows the request method, but the target resource doesn't support this method". You get this error when you attempt, for instance, to send a POST request to a GET route (as shown in your first example). This, however, is not the only issue with your code (on both client and server sides). Below is given an example on how to achieve what you described in the question using Path parameters. The same could be achieved using Query parameters, as well as Request Body. Please have a look at Python requests documentation on how to specify the parameters/body for each case. I would also highly suggest to take the FastAPI tutorial online—you'll find most of the answers you are looking for there.
app.py
from fastapi import FastAPI
app = FastAPI()
#app.get("/ping/{ping_id}")
async def get_trigger(ping_id: str):
return {"ping_id": ping_id}
test.py
import requests
url = 'http://127.0.0.1:8000/ping/test1'
resp = requests.get(url=url)
print(resp.json())

Related

Handling variable Path Parameter in Python FastApi

I'm trying to write a simple get request for my Angular Frontend in FastApi
i've created this endpoint with the parameter of the item_id:
#app.get("/pokemon/{item_id}")
async def getPokemon(item_id: int):
response = pokemon.getPokemon()
return response
and in the getPokemon() i go to the official Api and make the get Request:
def getPokemon():
response = requests.get('https://pokeapi.co/api/v2/pokemon/{item_id}'),
pokemonOutput = json.loads(response.text)
return pokemonOutput
My Question is, if i make the request to my endpoint and send the item_id parameter from the frontend with it. How can i make it so the item_id is passed as variable in the url of the get Request to the official API?
I can't seem to find anything by googling.
Thx for helping!
you just modify the function
def get_pokemon(item_id):
response = requests.get('https://pokeapi.co/api/v2/pokemon/'+item_id),
pokemonOutput = json.loads(response.text)
return pokemonOutput
and call it from you're endpoint

FastAPI: Error by sending Request Body trough GET

I'm updating some APIs that I have coded using Python and FastAPI. In the actual version I'm sending the info using Query Paramenters, but now I want to try to send the data by using Request Body, but the code doesn't seem to work.
Below you can see a sample of the code on the Server Side where FastAPI should return the text I'm sending (for this example only the "string_to_show" info, but in the real project there would be more fields). I know that is a base code, but this is just a sample.
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel
import uvicorn
app = FastAPI()
class req_body(BaseModel):
string_to_show:str
#app.get("/test/")
def scraper_shield(payload:req_body):
request_feedback = str(payload.string_to_show)
return request_feedback
if __name__ == "__main__":
uvicorn.run(app)
On the Client Side I'm using this code, that simply sends the payload to the server.
import requests
payload = {'string_to_show':'Python is great!'}
r = requests.get('http://127.0.0.1:8000/test/', params=payload)
print(r.text)
When sending the request, I should get the string "Python is Great!" but instead I'm getting some errors, below the Client and Server messages:
CLIENT: {"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}
SERVER: "GET /test/?string_to_show=Python+is+great%21 HTTP/1.1" 422 Unprocessable Entity
GET methods are not suppose to have body.
https://dropbox.tech/developers/limitations-of-the-get-method-in-http
POST method is meant for that.
If you really need some robust parametrization with GET (but I really would reconsider that), think about putting them into custom header(s).
You are sending the parameters as query strings when using
r = requests.get('http://127.0.0.1:8000/test/', params=payload). You have to instead use
r = requests.get('http://127.0.0.1:8000/test/', json=payload) which will create a request body and send it correctly. This what you also see from the errors, which basically tell you that the required body is missing.

Unit testing Python Azure Function: How do I construct a mock test request message with a JSON payload?

I want to unit test my Python Azure function. I'm following the Microsoft documentation.
The documentation mocks the call to the function as follows
req = func.HttpRequest(
method='GET',
body=None,
url='/api/HttpTrigger',
params={'name': 'Test'})
I would like to do this but with the parameters passed as a JSON object so that I can follow the req_body = req.get_json() branch of the function code. I guessed I would be able to do this with a function call like
req = func.HttpRequest(
method='GET',
body=json.dumps({'name': 'Test'}),
url='/api/HttpTrigger',
params=None)
If I construct the call like this, req.get_json() fails with the error message AttributeError: 'str' object has no attribute 'decode'.
How do I construct the request with JSON input parameters? It should be trivial but I'm clearly missing something obvious.
If I construct my mock call as follows:
import json
req = func.HttpRequest(
method='POST',
body=json.dumps({'name': 'Test'}).encode('utf8'),
url='/api/HttpTrigger',
params=None)
Then I am able to make a successful call to req.get_json(). Thanks to #MrBeanBremen and #JoeyCai for pointing me in the correct direction i.e. don't call GET and make the message a byte string.
Any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
For your http request, it is a GET method. You can send a request body with GET but it should not have any meaning.
So use the below code to construct a mock HTTP request with a json payload. For more details, you could refer to this article.
req = func.HttpRequest(
method='GET',
body=None,
url='/api/HttpTrigger',
params={'name': 'Test'})
Update:
For Post request, you could send json payload with body=json.dumps({'name': 'Test'}).encode('utf8') while body expects a byte string:
req = func.HttpRequest(
method='POST',
body=json.dumps({'name': 'Test'}).encode('utf8'),
url='/api/HttpTrigger',
params=None)

Check the Integrity of Message to an API Response message using JWT-extended in Flask

Assuming that I have an API endpoint api.example.com/v1/data and a GET method with #jwt-required similar to this:
from flask_jwt_extended import jwt_required
from flask_restful import Resource
class Data(Resource):
#jwt_required
def get(self):
"""
GET Response message.
"""
return {"message":"important-info", "ts":datetime}, 200
So to GET this message you need to authenticate yourself with a Bearer "access_token" in the request's header.
How could I create an HMAC for this message. Ideally I would like to add the access token, so to check the integrity of the whole message.
So I would like to have an extra field in the returned JSON called checksum with a value hash(whole_message).
You can use Flask's after_request to register a function that processes the response after it was generated by the view.
For example, to do exactly what you ask for (I am using built-in python's hash function, you can import/write your own as needed):
#app.after_request
def after_request(response):
data = json.loads(response.get_data())
data['checksum'] = hash(response.get_data())
response.set_data(json.dumps(data))
return response
However, you will have to make sure to always return a dictionary for this to work. Here are a couple alternatives:
1) Include the view's response inside another json, e.g.:
#app.after_request
def after_request(response):
data = json.loads(response.get_data())
data = {
'response': data,
'checksum': hash(response.get_data())
}
response.set_data(json.dumps(data))
return response
2) Add the checksum to the response headers (I would go with this one). E.g.:
#app.after_request
def after_request(response):
response.headers['Checksum'] = hash(response.get_data())
return response
As a final note, if you want to hash the response using the access token, as you state in your question, you can access this token from the request object, like so:
from flask import request
access_token = request.headers.get('Authorization')
So now you can use access_token in whatever way you need.

bottle request.json getting a 405 on post

I'm trying to get bottle to receive json in an xmlhttprequest and I'm getting a 405 error
Part of my bottle script:
#app.route('/myroute/')
def myroute():
print request.json
Part of my other script to test the xhr out:
jdata = json.dumps({"foo":"bar"})
urllib2.urlopen("http://location/app/myroute/", jdata)
Why am I getting a 405?
bottlepy error: 127.0.0.1 - - [2012-09-23 23:09:34] "POST /myroute/ HTTP/1.0" 405 911 0.005458
urllib2 error: urllib2.HTTPError: HTTP Error 405: Method Not Allowed
I also tried variations of:
#app.route('/myroute/json:json#[1-9]+#')
def myroute(json):
request.content_type = 'application/json'
print request.json, json
Returning json does not seem to be an issue
I think the problem is the server does not allow POST requests. You can probably try sending it in a GET request instead:
urllib2.urlopen("http://location/app/myroute/?" + jdata)
UPDATE:
I just realized, after looking at your question again, that you are actually trying to send JSON data via GET request. You should in general avoid sending JSONs with GET requests, but use POST requests instead[Reference].
To send a POST request to Bottle, you also need to set the headers to application/json:
headers = {}
headers['Content-Type'] = 'application/json'
jdata = json.dumps({"foo":"bar"})
urllib2.urlopen("http://location/app/myroute/", jdata, headers)
Then, with the help of #Anton's answer, you can access the JSON data in your view like this:
#app.post('/myroute/')
def myroute():
print request.json
Also, as a bonus, to send a normal GET request and access it:
# send GET request
urllib2.urlopen("http://location/app/myroute/?myvar=" + "test")
# access it
#app.route('/myroute/')
def myroute():
print request.GET['myvar'] # should print "test"
By default, the route decorator makes the decorated function handle only GET requests. You need to add a method argument to tell Bottle to handle POST requests instead. To do that, you need to change:
#app.route('/myroute/')
to:
#app.route('/myroute/', method='POST')
or a shorter version:
#app.post('/myroute/')

Categories

Resources