serve webpage using hypercorn (without any framework) - python-3.x

The code in the documentation is:
A very simple ASGI app that simply returns a response containing hello is, (file hello_world.py)
async def app(scope, receive, send):
if scope["type"] != "http":
raise Exception("Only the HTTP protocol is supported")
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'text/plain'),
(b'content-length', b'5'),
],
})
await send({
'type': 'http.response.body',
'body': b'hello',
})
simply run via
hypercorn hello_world:app
But the problem is, i do not want to handle type, status or headers.
I can do it with fastapi. Here, i do not need to handle type, status or headers. Given the file name is hello_world.py
from fastapi import FastAPIapp = FastAPI()
#app.get("/")
async def root():
return {"message": "Hello world!"}
simply run using
hypercorn hello_world:app
How can I have this fastapi like behaviour without fastapi. I mean is there any simple http parser which i can use instead of fastapi. Custom code would be even better.

Related

Using aiohttp, parse response body as a dynamic type

My goal is to dynamically parse the HTTP response body from the return value of a aiohttp.ClientSession.get call. Is there a builtin method for achieving this? In the following example I've achieved this logic:
import aiohttp
import asyncio
async def get_req(url, parse_as='json', params=None):
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as r:
parse_methods = {
'json': r.json,
'binary': r.read,
'text': r.text
}
target_method = parse_methods.get(parse_as)
return await target_method()
asyncio.run(get_req('<some_link>', 'text')) # Will return text data from body of response.
My brain tells me this is not the best approach, but so far I haven't found any examples explaining how to set a dynamic parse type for aiohttp.ClientSession's HTTP methods. I want to keep the aiohttp session local to get_req.
My overall question is: How do I do achieve this logic in a more readable and efficient way?

Unable to verify Discord signature for bot on AWS Lambda Python 3 (Interactions_Endpoint_URL)

I am attempting to validate the signature for my bot application using discord's "INTERACTIONS ENDPOINT URL" in a lambda function running python 3.7. Using the documentation here under the "Security and Authorization" section, I still seem to be unable to get a valid return on the signature, with the exception being triggered each time. I'm unsure which aspect of the validation is incorrect. I am using AWS API Gateway to forward the headers to the lambda function in order to access them. Any help with pointing me in the right direction would be appreciated.
Edit:
Here is the output of the event in lambda for reference. I removed some of the values for security marked by <>.
{'body': {'application_id': '<AppID>', 'id': '<ID>', 'token': '<Token>', 'type': 1, 'user': {'avatar': '4cbeed4cdd11cac74eec2abf31086e59', 'discriminator': '9405', 'id': '340202973932027906', 'public_flags': 0, 'username': '<username>'}, 'version': 1}, 'headers': {'accept': '*/*', 'content-type': 'application/json', 'Host': '<AWS Lambda address>', 'User-Agent': 'Discord-Interactions/1.0 (+https://discord.com)', 'X-Amzn-Trace-Id': 'Root=1-60a570b8-00381f6e26f023df5f9396b1', 'X-Forwarded-For': '<IP>', 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https', 'x-signature-ed25519': 'de8c8e64be2058f40421e9ff8c7941bdabbf501a697ebcf42aa0419858c978e19c5fb745811659b41909c0117fd89430c720cbf1da33c9dcfb217f669c496c00', 'x-signature-timestamp': '1621455032'}}
import json
import os
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
def lambda_handler(event, context):
# Your public key can be found on your application in the Developer Portal
PUBLIC_KEY = os.environ['DISCORD_PUBLIC_KEY']
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY))
signature = event['headers']["x-signature-ed25519"]
timestamp = event['headers']["x-signature-timestamp"]
body = event['body']
try:
verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature))
except BadSignatureError:
return (401, 'invalid request signature')
I was able to diagnose the issue. I was unable to verify the signature because AWS API Gateway was altering the body into JSON before it got to my lambda function. This made the signature verification come up as invalid each time. I solved this by checking Lambda Proxy Integration in the Integration Request section in API Gateway. Lambda Proxy Check Box. This allowed an unaltered body being sent to Lambda, which I could then verify my discord outgoing webhook. Below is my final code.
import json
import os
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
def lambda_handler(event, context):
PUBLIC_KEY = os.environ['DISCORD_PUBLIC_KEY']
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY))
signature = event['headers']["x-signature-ed25519"]
timestamp = event['headers']["x-signature-timestamp"]
body = event['body']
try:
verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature))
body = json.loads(event['body'])
if body["type"] == 1:
return {
'statusCode': 200,
'body': json.dumps({'type': 1})
}
except (BadSignatureError) as e:
return {
'statusCode': 401,
'body': json.dumps("Bad Signature")
}

Web API with Python Flask POST request 405 Method Not Allowed

I am making a Web API for a python program I wrote, I am copying a tutorial
This is the API code
#!flask/bin/python
from flask import Flask
from flask import make_response
from flask import request
import requests
import json
app = Flask(__name__)
#app.route('/')
def index():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True)
#app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
#app.route('/5492946838458/index.html', methods=['POST'])
def create_task():
if not request.json or not 'key' in request.json or not 'name' in request.json or not 'text' in request.json or not 'pack' in request.json:
abort(400)
if 'title' in request.json and type(request.json['title']) != unicode:
abort(400)
if 'description' in request.json and type(request.json['description']) is not unicode:
abort(400)
task = {
'key': request.json['key'],
'name': request.json['name'],
'text': request.json['text'],
'pack': request.json['pack']
}
return (200)
This is the URL I'm sending it to
https://my.websites.url.here/5492946838458/
and the json data I'm sending
{
"key": "key",
"name": "name",
"text": "text",
"pack": "pack"
}
and the headers I get back I get
date: Fri, 04 Sep 2020 17:48:30 GMT
content-length: 0
vary: Origin
accept-ranges: bytes
allow: GET, HEAD, OPTIONS
Why does this happen and how can I fix this
Two problems I can see...
This line shouldn't be floating in the middle of your code. It should be at the very end:
if __name__ == '__main__':
app.run(debug=True)
With its current placement, if you're executing the app with python app.py, the app will run at this point. Routes before it (index) will be available, however routes declared after it (create_task) will not (until you kill the server - when the latter route is added, right before the python process stops).
This problem wouldn't be seen if executing with flask run as the if clause is False.
#app.route('/5492946838458/index.html', methods=['POST'])
for this one you're probably want:
#app.route('/5492946838458/', methods=['POST'])
This declares the URL of that route.
Now a request to https://my.websites.url.here/5492946838458/ should return a successful response. A request to /5492946838458 will return a 308 redirect to the one with the trailing slash.
I'm not sure why you were getting 405 before. Perhaps there's aother route somewhere in your code accepting the request, but not the method.

Axios POST is not parsed correctly in Python Flask

Intro
Hi, I can see this question asked multiple times, but not a proper solution.
whats wrong
GET requests are working as expected. but if I use Vue.js and Axios to send a POST request to a flask server.
POST with JSON sent with Postman also works..
heres the axios call:
async submit() {
await this.$axios.post("/bob/posting", { foo: "bar" }).then(response => {
console.log(response)
})
}
On the flask side:
from flask import Flask, request
from flask_cors import CORS,cross_origin
app = Flask(__name__)
cors = CORS(app, resources={r"//*": {"origins": "*"}})
#app.route('/bob/posting', methods=['POST'])
def handleRoute():
print(request.is_json)
return (
request.args
or request.form
or request.get_json(force=True, silent=True)
or request.data
)
The results at the flask side:
True will be printed (indicating a json request)
nothing will be returned (also, for each option I tried printing it individually just in case)
Q
How can I properly use variables received from an axios json request on a flask server?
Did not at all understand why, but adding a text/plain content type fixed it.. what...
async submit() {
await this.$axios.post("/bob/posting", { foo: "bar" }, {'content-type':'text/plain'}).then(response => {
console.log(response)
})
}

aiohttp api error handler framework

we have developed some of the aiohttp server side apis and from that api we am calling one of the python class, where i have done all business logic.
now we want to create a error handling framework for all apis, please give some ideas to implement that framework and i need to do request parameters validations as well, should i consolidate and send back all error at one time or just check one parameter send back the error message to caller.
api look like this:
async def new_user(request):
try:
# happy path where name is set
user = request.query['name']
# Process our new user
print("Creating new user with name: " , user)
response_obj = { 'status' : 'success' }
# return a success json response with status code 200 i.e. 'OK'
return web.Response(text=json.dumps(response_obj), status=200)
except Exception as e:
# Bad path where name is not set
response_obj = { 'status' : 'failed', 'reason': str(e), 'code' : 400 }
# return failed with a status code of 500 i.e. 'Server Error'
return web.Response(text=json.dumps(response_obj), status=400)
If you are using aio-http try to create aiohttp.web.middleware.
https://docs.aiohttp.org/en/stable/web_advanced.html#middlewares

Resources