I am working on a middleware where i need to convert the HTTPResponse(ex: 500 internal error) to a JSONResponse like below
{
"error":"some error string",
"traceback":"complete traceback of exception"
}
Can someone please guide me how i can achieve this?
We can use the EXCEPTION_HANDLER in REST for this job.
REST_FRAMEWORK =
{
'EXCEPTION_HANDLER': 'core.middlewares.custom_exception_handler'
}
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if not response:
response_data = dict()
exc_tb = tb.format_exc()
response_data['status'] = 'failed'
response_data['code'] = 500
response_data['errors'] = [{'server_error': str(exc)}]
response_data['traceback'] = exc_tb
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data=response_data)
return response
Related
I am working on a blog project, following a certain tutorial, using django 4.1.4, DRF-3.14, React 18.2, and using drf-social-oauth2 1.2.1 module for social logins
When I use postman or that DRF API Interface to make request to the api everything is working fine, I can do all the request when authenticated but when I use exios instance I keep getting the 403 Forbidden error.
Here is axios file:
import axios from 'axios';
const baseURL = 'http://127.0.0.1:8000/api/';
const token = localStorage.getItem('access_token')
console.log(token);
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'multipart/form-data',
accept: 'application/json',
},
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error.config;
if (typeof error.response === 'undefined') {
alert(
'A server/network error occurred. ' +
'Looks like CORS might be the problem. ' +
'Sorry about this - we will get it fixed shortly.'
);
return Promise.reject(error);
}
if (
error.response.status === 401 &&
originalRequest.url === baseURL + 'token/refresh/'
) {
window.location.href = '/login/';
return Promise.reject(error);
}
if (
error.response.data.code === 'token_not_valid' &&
error.response.status === 401 &&
error.response.statusText === 'Unauthorized'
) {
const refreshToken = localStorage.getItem('refresh_token');
if (refreshToken) {
const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));
// exp date in token is expressed in seconds, while now() returns
milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
if (tokenParts.exp > now) {
return axiosInstance
.post('/token/refresh/', {
refresh: refreshToken,
})
.then((response) => {
localStorage.setItem('access_token',
response.data.access_token);
localStorage.setItem('refresh_token',
response.data.refresh_token);
return axiosInstance(originalRequest);
})
.catch((err) => {
console.log(err);
});
} else {
console.log('Refresh token is expired', tokenParts.exp, now);
window.location.href = '/login/';
}
} else {
console.log('Refresh token not available.');
window.location.href = '/login/';
}
}
// specific error handling done elsewhere
return Promise.reject(error);
}
);
export default axiosInstance;
API Views file:
from rest_framework import generics
from posts.models import Post
from . serializers import PostSerializer
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.permissions import SAFE_METHODS, IsAuthenticated, BasePermission,
IsAuthenticatedOrReadOnly
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from rest_framework import filters
from rest_framework import status
class PostUserWritrPermission(BasePermission):
message = 'Editing post is restricted to the author only.'
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.author.user == request.user
class PostList(generics.ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = PostSerializer
queryset = Post.objects.all()
class PostDetail(generics.RetrieveAPIView):
serializer_class = PostSerializer
def get_object(self, queryset=None, **kwargs):
slug = self.kwargs.get('pk')
return get_object_or_404(Post, slug=slug)
class PostListDetailFilter(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['^slug']
class CreatePost(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [MultiPartParser, FormParser]
def post(self, request, format=None):
print(request.data)
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class AdminPostDetail(generics.RetrieveAPIView):
permission_classes = [IsAuthenticated]
queryset = Post.objects.all()
serializer_class = PostSerializer
class EditPost(generics.UpdateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = PostSerializer
queryset = Post.objects.all()
class DeletePost(generics.RetrieveDestroyAPIView):
permission_classes = [IsAuthenticated]
serializer_class = PostSerializer
queryset = Post.objects.all()
Part of the Settings file:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
AUTHENTICATION_BACKENDS = [
'drf_social_oauth2.backends.DjangoOAuth2',
'django.contrib.auth.backends.ModelBackend',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'drf_social_oauth2.authentication.SocialAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:8000",
"http://127.0.0.1:8000",
]
I have the following code:
class CampaignModelTests(TestCase):
# https://docs.djangoproject.com/en/4.1/topics/testing/advanced/
def setUp(self):
# create some test campaigns
shop = Shop.objects.create(name='Test Shop')
self.factory = RequestFactory()
self.user = User.objects.create(shop_username='testuser', shop=shop)
self.merchant = User.objects.create(username='merchant', password='merchant123', is_merchant=True, shop=shop)
self.campaign = Campaign.objects.create(name='Test Campaign', shop=shop)
def test_campaign_creation(self):
request = self.factory.get(reverse('create-campaign'), {
'name': 'Test Campaign -1',
'shop': Shop(name='Test Shop').sid
})
request.user = self.merchant
print('request.user: ', request.user.is_merchant)
response = CampaignCreate.as_view()(request)
self.assertEqual(response.status_code, 200)
And I am getting the following error:
return request.user.is_merchant or request.user.is_superuser
AttributeError: 'AnonymousUser' object has no attribute 'is_merchant'
I have tried using self.client as well but still the same error.
self.client.force_login(self.merchant)
# send a request to the view
response = self.client.post(reverse('create-campaign'), {
'name': 'Test Campaign -1',
'shop': Shop(name='Test Shop').sid
})
self.assertEqual(response.status_code, 200)
I tried this response = Response() method but it is not set in the browser cookie
Because my function return user and token instead of response.Any possibility to set cookie in browser without response or httpResponse?..
views.py(It does not work):
def authenticate(self, request):
......
new_access_token = result.get('access')
response = Response()
response.set_signed_cookie(
key = settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS'],
value = new_access_token,
salt = settings.SIMPLE_JWT['AUTH_COOKIE_SALT'],
expires = 214748364,
secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
)
return self.get_user(validated_token),validated_token
-- Full View.py(above is short) --
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken
from django.conf import settings
import requests
import json
from rest_framework.response import Response
from .utility import set_browser_cookie
class CustomAuthentication(JWTAuthentication):
def authenticate(self, request):
header = self.get_header(request)
if header is None:
raw_token = request.get_signed_cookie(settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS'],salt = settings.SIMPLE_JWT['AUTH_COOKIE_SALT'],default = False) or None
else:
raw_token = self.get_raw_token(header)
if raw_token is None:
return None
try:
validated_token = self.get_validated_token(raw_token)
except InvalidToken:
refresh = request.get_signed_cookie(settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH'],salt = settings.SIMPLE_JWT['AUTH_COOKIE_SALT'],default = False)
if refresh is None:
return None
protocol = 'https' if request.is_secure() else 'http'
domain_name = settings.SITE_DOMAIN_NAME
url = f'{protocol}://{domain_name}/auth/api/token/refresh/'
data = {"refresh": refresh}
resp = requests.post(
url,
data=json.dumps(data),
headers = {'content-type': 'application/json'}
)
result = resp.json()
new_access_token = result.get('access')
if new_access_token is None:
return None
validated_token = self.get_validated_token(new_access_token)
response = Response()
response.set_signed_cookie(
key = settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS'],
value = new_access_token,
salt = settings.SIMPLE_JWT['AUTH_COOKIE_SALT'],
expires = 214748364,
secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
)
return self.get_user(validated_token),validated_token
Please help me I am very confuse for this.
I got a JSON response from some url. I have to show that into rest api but i got error. Here is my code
views
class StoreView(APIView):
serializer_class = PostcodeLookupSerializer
resp = requests.get('https://api.postcodes.io/postcodes/BN14 9GB')
resp_data = resp.json()['result']
result_dic = {
'longitude': resp_data['longitude'],
'latitude': resp_data['latitude']
}
result_data = JsonResponse(result_dic)
def result(self):
json_data = self.resp_data()
file_serializer = PostcodeLookupSerializer(json_data, many=True)
return Response(data=file_serializer.data, status=status.HTTP_200_OK)
serializer
class PostcodeLookupSerializer(serializers.Serializer):
postcode = serializers.CharField(required=True)
name = serializers.CharField(required=True)
and url
urlpatterns = [
path('views/', StoreView.as_view(), name='postcode_lookup'),]
how to display a json response into restapi?
I got this error
"detail": "Method \"GET\" not allowed."
You should return data inside response as below
return Response(data=file_serializer.data, status=status.HTTP_200_OK)
I'm using flask-jwt and flask-restful
This is how flask-jwt handling errors(taken from its github repo) but none of them let me take the parameters of JWTError.
if auth is None:
raise JWTError('Authorization Required', 'Authorization header was missing', 401, {
'WWW-Authenticate': 'JWT realm="%s"' % realm
})
parts = auth.split()
if parts[0].lower() != auth_header_prefix.lower():
raise JWTError('Invalid JWT header', 'Unsupported authorization type')
elif len(parts) == 1:
raise JWTError('Invalid JWT header', 'Token missing')
elif len(parts) > 2:
raise JWTError('Invalid JWT header', 'Token contains spaces')
try:
handler = _jwt.decode_callback
payload = handler(parts[1])
except SignatureExpired:
raise JWTError('Expired JWT', 'Token is expired', 401, {
"WWW-Authenticate": 'JWT realm="{0}"'.format(realm)
})
except BadSignature:
raise JWTError('Invalid JWT', 'Token is undecipherable')
_request_ctx_stack.top.current_user = user = _jwt.user_callback(payload)
if user is None:
raise JWTError('Invalid JWT', 'User does not exist')
And the following are the different ways how I try to handle JWTError
In flask:
def handle_user_exception_again(e):
if isinstance(e, JWTError):
data = {'status_code': 1132, 'message': "JWTError already exists."}
return jsonify(data), e.status_code, e.headers
return e
app.handle_user_exception = handle_user_exception_again
In flask-restful (handle_error)
class FlaskRestfulJwtAPI(Api):
def handle_error(self, e):
if isinstance(e, JWTError):
code = 400
data = {'status_code': code, 'message': "JWTError already exists."}
elif isinstance(e, KeyError):
code = 400
data = {'status_code': code, 'message': "KeyError already exists."}
else:
# Did not match a custom exception, continue normally
return super(FlaskRestfulJwtAPI, self).handle_error(e)
return self.make_response(data, code)
In flask-restful (error_router)
class FlaskRestfulJwtAPI(Api):
def error_router(self, original_handler, e):
print(type(e))
if e is JWTError:#KeyError:
data = {
"code":400,
"message":"JWTError"
}
return jsonify(data), 400
elif isinstance(e,KeyError):
data = {
"code":400,
"message":"KeyError"
}
return jsonify(data), 400
else:
return super(FlaskRestfulJwtAPI, self).error_router(original_handler, e)
I see that you are trying to raise an HTTPException from these errors, which helps with returning them to users. To raise an HTTP exception, you can catch all errors and if they are an instance of a JWTError, you can convert them to an instance of an HTTPException using a function like the following:
def convert_to_http_exception(e):
if isinstance(e, JWTError):
jwtdescription = e.error + ": " + e.description
http_exception = HTTPException(description=jwtdescription)
http_exception.code = e.status_code
return http_exception
else:
return e