No "Authorization" header, how to access authorization header? Django - django

I need to check the Authorization HTTP Header of every incoming request.
First i have implemented Middleware. Now on website in devtools (when i post something) i see authorizational header with token.
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
user_id = request.POST.get('created_by', False)
try:
api_token = CustomUser.objects.get(user=user_id).api_token
except MyUser.DoesNotExist:
api_token = ''
response = self.get_response(request)
response['Authorization'] = "Bearer " + api_token
return response
class MyApiView(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Event.objects.all()
serializer_class = EventSerializer
#action(methods=['POST'], detail=False)
def post(self, request):
print(request.META['HTTP_AUTHORIZATION']) **#keyerror**
print(request.META['Authorization']) **#keyerror**
print(request.headers.items()) **#no authorization header**
tutorial_serializer = MyApiSerializer(data=request.data)
if tutorial_serializer.is_valid():
tutorial_serializer.save()
return Response(tut`enter code here`orial_serializer.data, status=status.HTTP_201_CREATED)
return Response(tutorial_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

You're assigning header to wrong entity. Instead of adding header to response (what Django will return back to client), you need to add it to request headers:
from django.utils.deprecation import MiddlewareMixin
class CustomHeaderMiddleware(MiddlewareMixin):
def process_request(self, request):
user_id = request.POST.get('created_by', False)
try:
api_token = CustomUser.objects.get(user=user_id).api_token
except CustomUser.DoesNotExist:
api_token = ''
request.META['HTTP_Authorization'] = "Bearer " + api_token
response = self.get_response(request)
return response

Related

Django Middleware does not modify request in tests

I am trying to create test class for my custom middleware. The project is using Django REST framework. Middleware class works fine when server is running, but when I run test it behaves not quite as I would expect it to do. Maybe I misunderstood something, as I am quite new to testing in Django.
my_middleware.py:
class FX:
a = False
b = None
c = ''
def __init__(self) -> None:
pass
def __str__(self):
return 'fx ok'
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.fx = FX()
response = self.get_response(request)
print('done')
return response
views.py:
class TestView(APIView):
def get(self, request, format=None):
print('View ok')
print('FX: ', request.fx)
return Response({'result':'ok'})
tests.py:
class TestMyMiddleware(APITestCase):
#classmethod
def setUpTestData(cls):
pass
def setUp(self):
pass
def test_fx(self):
response = self.client.get(reverse('TestView'), content_type="application/json")
request = response.request
self.assertTrue(hasattr(request, 'fx'))
The code above actually runs the middleware. It prints "done" form the middleware call, then prints 'View ok' and also prints FX instance. However request.fx is not available in the test_fx method, thus giving assertion failure:
self.assertTrue(hasattr(request, 'fx'))
AssertionError: False is not true
Any idea what I might be doing wrong?
You need to access the request object from the response with response.wsgi_request instead of response.request.
class TestMyMiddleware(APITestCase):
#classmethod
def setUpTestData(cls):
pass
def setUp(self):
pass
def test_fx(self):
response = self.client.get(reverse('TestView'), content_type="application/json")
request = response.wsgi_request
self.assertTrue(hasattr(request, 'fx'))

Insert custom field in Django response using middleware

I am looking for the right directions to add a custom field in the HTTP response using middleware and access the custom field in the JavaScript front-end.
I am trying to implement this, but on receiving the response on the JavaScript side there is no field like "is_logged" in the body.
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.user.is_authenticated:
response = self.get_response(request)
response.body['is_logged'] = True
else:
response = self.get_response(request)
response.body['is_logged'] = False
return response

Post request handling in Django Rest framework

I am using Django Rest Framework, currently to pull some data from the backend we are using Get request, but due to URL limit going high we are planning to implement a Post request. To do this firstly the backend Django Rest API has to be made available to serve a post request.
I am new to Django and I don't find a post or get method in the code, all i can say is we are using viewsets, I tried using "#detail_route(methods=['post'])" but this didn't work, what am I doing wrong here?
class XViewSet(viewsets.ViewSet):
renderer_classes = ''
def retrieve(self, request, pk=None):
try:
pk = int(pk)
except ValueError:
raise InvalidParameterError(parameter_name='id', invalid_value=pk)
queryset = models.X.objects.all()
x = get_object_or_404(queryset, pk=pk)
pipelines = request.query_params.getlist('pipeline[]')
callsets =
callset_ids =
serializer = serializers.XSerializer(x, context={'request': request})
requested_samples = [z[1:] for z in request.query_params.getlist('s')]
filtered_calls = []
serialized_data = serializer.data
unfiltered_calls = serialized_data.get('calls')
if unfiltered_calls:
for serialized_calls in unfiltered_calls:
if serialized_calls['callset'] in callset_ids:
unfiltered_calls = serialized_calls['data']
for call in unfiltered_calls:
if call['y'] in requested_y:
filtered_calls.append(call)
break
serialized_data['calls'] = filtered_calls
return Response(serialized_data, status=status.HTTP_200_OK)
def list(self, request):
qp = self.request.query_params
validated_qp =
# generate the query
query_object =
query =
# execute the query
cursor = connections['default'].cursor()
cursor.execute(query)
qs = utils.dictfetchall(cursor)
# sanitize query results
if 't' in validated_qp:
return_data =
else:
for x in qs:
if 'calls' in x:
x['calls'] =
else:
x['calls'] = {}
return_data =
resp = Response(return_data, status=status.HTTP_200_OK)
if validated_qp.get(''):
resp['content-disposition'] =
return resp
You can use class-based views to handle the requirements,
from rest_framework.views import APIView
class MyAPI(APIView):
def get(selfself, request):
# do stuff with get
return Response(data="return msg or data")
def post(self, request):
post_data = request.data
# do something with `post_data`
return Response(data="return msg or data")
UPDATE : Using ViewSet
ViewSet class provide us create() methode to create new model instances. So we can override that to handle post data coming to the view. Just add a create() under your view class as below
class XViewSet(viewsets.ViewSet):
renderer_classes = ''
def create(self, request): # Here is the new update comes <<<<
post_data = request.data
# do something with post data
return Response(data="return data")
def retrieve(self, request, pk=None):
# your code
return Response(serialized_data, status=status.HTTP_200_OK)
def list(self, request):
# your code
return resp

Include a location header in Django Rest Framework

I read in a post that it is ideal to put that it is a good standard to include a Location header that points to the URL of the new resource (newly created via POST). My problem is I do not know how to include it.
I am using a class-based views using the APIView and my code in the view is:
class ListArtists(APIView):
serializer_class = ArtistSerializer
def get(self, request, format=None):
_array = Artist.objects.filter()
serializer = self.serializer_class(_array, many=True)
if serializer.data:
_status = status.HTTP_200_OK
else:
_status = status.HTTP_204_NO_CONTENT
return Response(standardResponse(data=serializer.data), status=_status)
def post(self, request, format=None):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(standardResponse(data=serializer.data), status=status.HTTP_201_CREATED)
else:
return Response(standardResponse(errors=serializer.errors))
artist = ListArtists.as_view()
urls.py
from django.conf.urls import url, include
from store import views
urlpatterns = [
url(r'^artists/', views.artist, name='artists-list'),
]
P.S.
Every time I throw a request using my Advanced REST Client this is the response that I receive:
Date: Sat, 23 Jul 2016 10:54:23 GMT
Server: WSGIServer/0.1 Python/2.7.10
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Allow: GET, POST, HEAD, OPTIONS
You can add arbitrary headers to a Response object, like so:
def post(self, request, format=None):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
obj = serializer.save()
response = Response(standardResponse(data=serializer.data),
status=status.HTTP_201_CREATED)
# If you have defined a get_absolute_url method on your model, then
# you can use that to get a URL for the new object
response['Location'] = obj.get_absolute_url()
return response
For those using the drf CreateModelMixin the view has to look something like that:
In case you do want an empty body in the response:
class MyView(mixins.CreateModelMixin, generics.GenericAPIView):
serializer_class = MySerializer
def post(self, request, *args, **kwargs):
default_response = self.create(request, *args, **kwargs)
return Response(headers={'Location': reverse("get_enpoint_name", args=[default_response.data['id']], request=self.request)})
In case the response body has to include the objects data as specified in the serializer:
class MyView(mixins.CreateModelMixin, generics.GenericAPIView):
serializer_class = MySerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def get_success_headers(self, data):
try:
return {'Location': reverse("customer", args=[data['id']], request=self.request)}
except (TypeError, KeyError):
return {}
Always open for improvements!

Tastypie to respond for every requests in Json format

I am working on Api for my project, i'm using Tastypie 9.9.0. I want the response in Json format for PUT, POST and DELETE operations.
The existing responses like STATUS 201 CREATED, STATUS 204 NO CONTENT, STATUS 410 GONE is fine.
It must respond in a custom format.
for example
1. {
"resource_name": "user",
"action":"password_reset",
"status": "success"
}
2. {
"resource_name": "todo",
"action":"insert",
"status":"sucess",
}
3. {
"resource_name": "todo",
"action":"delete",
"status":"sucess",
}
this is the code i was working on. I dont know how to add custom response messages
class ToDoResource(ModelResource):
user = fields.ToOneField(UserResource, 'user')
class Meta:
queryset = ToDo.objects.all()
fields=['alert_time','description','status','user']
resource_name = 'todo'
filtering = {
'user': ALL_WITH_RELATIONS,
'alert_time': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'],
}
serializer = Serializer()
authentication= MyApiKeyAuthentication()
authorization=Authorization()
always_return_data = True
allowed_methods = ['post','get','put','delete']
def obj_create(self, bundle,request=None, **kwargs):
if not request.user.is_superuser:
try:
bundle.data.pop('user')
except:
pass
return super(ToDoResource, self).obj_create(bundle, request, user=request.user)
def create_response(self, request, data):
"""
Extracts the common "which-format/serialize/return-response" cycle.
Mostly a useful shortcut/hook.
"""
desired_format = self.determine_format(request)
serialized = self.serialize(request, data, desired_format)
return HttpResponse(content=serialized, content_type=build_content_type(desired_format))
def apply_authorization_limits(self, request, object_list):
return object_list.filter(user=request.user)
You can add/modify custom data in get_list(request, **kwargs) and/or get_object(request, **kwargs)
For example,
import json
from django.http import HttpResponse
class ToDoResource(ModelResource):
# ... the rest of code
def get_list(self, request, **kwargs):
resp = super(ToDoResource, self).get_list(request, **kwargs)
data = json.loads(resp.content)
# ... the rest of code
data['meta']['resource_name'] = self._meta.resource_name
data['meta']['action'] = request.method
data['meta']['status'] = ANY_STATUS
# ... the rest of code
data = json.dumps(data)
return HttpResponse(data, mimetype='application/json', status=ANY_STATUS)