Flask Middleware Access Response Data - python-3.x

I am trying to create a middleware in flask to store request and response inside database. I am able to access data present inside request but unable to figure out how to access data present inside response. Response header showing content-length 1000 & if i am changing it then showing differences in the content-length.
import pprint
from werkzeug.wrappers import Request, Response
class LoggerMiddleware:
def __init__(self, application):
self.__application = application
def __call__(self, environ, start_response):
errors = environ['wsgi.errors']
length = int(environ.get('CONTENT_LENGTH', '0'))
data = environ['wsgi.input'].read(min(4096, length))
if data:
print(data)
pprint.pprint(('REQUEST', environ), stream=errors)
def _start_response(status, headers, *args):
pprint.pprint(('RESPONSE', status, headers), stream=errors)
return start_response(status, headers, *args)
return self.__application(environ, _start_response)

Related

How can I read response content (that is JSON) from Django middleware so I can convert it to HTML?

From my django view I am returning a serialized JSON as below:
def features(request):
features = db_service.fetch_all_features()
data = serializers.serialize('json', features)
return HttpResponse(data, content_type='application/json')
I have registered a middleware, where I want to fetch this JSON and convert it into HTML
class ReturnResponseAsHTML:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
#return HttpResponse(data, content_type='application/json')
return response
Once I have access to the response object, how may I fetch the JSON from it so I may convert it to HTML
response.getvalue()
where response is an instance of django.http.HttpResponse

How to set response status code in route with type json in odoo 14

I have created a route with type Json in odoo 14.
#http.route('/test', auth='public', methods=['POST'], type="json", csrf=False)
def recieve_data(self, **kw):
headers = request.httprequest.headers
args = request.httprequest.args
data = request.jsonrequest
So when I receive request using this route everything is fine, but suppose I want to return a different status code like 401, I could not do that with the route which type is json.
I have also tried the bellow method but the problem with this method is that it stops all odoo future requests.
from odoo.http import request, Response
#http.route('/test', auth='public', methods=['POST'], type="json", csrf=False)
def recieve_data(self, **kw):
headers = request.httprequest.headers
args = request.httprequest.args
data = request.jsonrequest
Response.status = "401 unauthorized"
return {'error' : 'You are not allowed to access the resource'}
So in the above example If I set the response status code to 401 all other requests even if they are to different routes will be stopped and its status code changes 401 .
I have checked this problem in both odoo14 and odoo13.
You cannot specify the code of the response, as you can see the status code is hardcoded in http.py:
def _json_response(self, result=None, error=None):
# ......
# ......
return Response(
body, status=error and error.pop('http_status', 200) or 200,
headers=[('Content-Type', mime), ('Content-Length', len(body))]
)
If the result is not an error the status code is always 200. but you can change the code of the method directly or use a monkey patch witch if it's not really important to specify the code of status in the result don't do ti ^^.
A simple monkey patch put this in __init__.py of the module that you want this behavior in it.
from odoo.http import JsonRequest
class JsonRequestPatch(JsonRequest):
def _json_response(self, result=None, error=None):
response = {
'jsonrpc': '2.0',
'id': self.jsonrequest.get('id')
}
default_code = 200
if error is not None:
response['error'] = error
if result is not None:
response['result'] = result
# you don't want to remove some key of another result by mistake
if isinstance(response, dict)
defautl_code = response.pop('very_very_rare_key_here', default_code)
mime = 'application/json'
body = json.dumps(response, default=date_utils.json_default)
return Response(
body, status=error and error.pop('http_status', defautl_code ) or defautl_code,
headers=[('Content-Type', mime), ('Content-Length', len(body))]
)
JsonRequest._json_response = JsonRequestPatch._json_response
and in the result of JSON api you can change the code of status like this
def some_controler(self, **kwargs):
result = ......
result['very_very_rare_key_here'] = 401
return result
The risk in monkey patching is you override the method compeletly and if some change is done in Odoo in new version you have to do you it in your method too.
The above code is from odoo 13.0 I did a lot of this when I build a restfull module to work with a website uses axios witch don't allow sending body in GET request. and odoo expect the argument to be inside the body in json request.

Using FastAPI to create custom decorators with variables and incoming payload

I am trying to add a custom decorator to secure fastapi endpoints to list of users. The incoming payload has JWT data in the header and/or cookies.
With out the decorator, fastapi_jwt_auth allows/denies to the endpoints as long as the token is valid. However when adding the custom decorator, it fails to pass the header/cookie payload to the decorator to be parsed and return the correct response if valid/invalid.
From what I could understand from this post it should be able to obtain the payload from the router function. However it only seems to create a blank class object.
from functools import wraps
from fastapi import Depends
from fastapi.responses import JSONResponse
from fastapi_jwt_auth import AuthJWT
def auth(users: Union[List[str], None, str] = None):
def decorator_auth(func):
#wraps(func)
def wrapper_auth(*args, **kwargs):
Authorize.jwt_required()
jwt_username = Authorize.get_raw_jwt()['username']
if jwt_username not in users:
return JSONResponse(
status_code=401,
content={"detail": "Unauthorized"} )
return func(*args, **kwargs)
return wrapper_auth
return decorator_auth
#app.get('/data')
def secure_data(Authorize: AuthJWT = Depends()):
Authorize.jwt_required()
return JSONResponse(
status_code=200,
content={"detail": "Payload only all with valid tokens."})
#app.get('/data1')
#auth(users=['user1', 'user3')
def secure_data_1(Authorize: AuthJWT = Depends()):
return JSONResponse(
status_code=200,
content={"detail": "Payload only for User 1 and User 3."})
#app.get('/data2')
#auth(users=['user2'])
def secure_data2(Authorize: AuthJWT = Depends()):
return JSONResponse(
status_code=200,
content={"detail": "Payload only for User 2."} )

Add variable to Django request object in Middleware (once)

class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
request.variable = 1
response = self.get_response(request)
return response
This works but it it processing the request twice. I am unsure of how to set this variable after the view has been processed (for every view), only once. process_template_response is not a valid option because it will not work with every view. Is there a better way to do this?

How to capture API failure while using oauthlib.oauth2 fetch_token

The Python3 fetch_token method in this library does not check the response status before consuming the response. If the API call it makes fails, then the response will be invalid and the script crashes. Is there something I can set so that an exception will be raised on a non-success response before the library can read the response?
import requests
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import BackendApplicationClient
from oauthlib.oauth2 import OAuth2Error
AUTH_TOKEN_URL = "https://httpstat.us/500" # For testing
AUTH = HTTPBasicAuth("anID", "aSecret")
CLIENT = BackendApplicationClient(client_id="anID")
SCOPES = "retailer.orders.write"
MAX_API_RETRIES = 4
class MyApp:
def __init__(self):
"""Initialize ... and obtain initial auth token for request"""
self.client = OAuth2Session(client=CLIENT)
self.client.headers.update(
{
"Content-Type": "application/json"
}
)
self.__authenticate()
def __authenticate(self):
"""Obtain auth token."""
server_errors = 0
# This needs more work. fetch_token is not raising errors but failing
# instead.
while True:
try:
self.token = self.client.fetch_token(
token_url=AUTH_TOKEN_URL, auth=AUTH, scope=SCOPES
)
break
except (OAuth2Error, requests.exceptions.RequestException) as e:
server_errors = MyApp.__process_retry(
server_errors, e, None, MAX_API_RETRIES
)
#staticmethod
def __process_retry(errors, exception, resp, max_retries):
# Log and process retries
# ...
return errors + 1
MyApp() # Try it out
You can add a "compliance hook" that will be passed the Response object from requests before the library attempts to parse it, like so:
def raise_on_error(response):
response.raise_for_status()
return response
self.client.register_compliance_hook('access_token_response', raise_on_error)
Depending on exactly when you may get errors, you might want to do this with 'refresh_token_response' and/or 'protected_request' as well. See the docstring for the register_compliance_hook method for more info.

Resources