I'm trying to verify that the required data was included in a POST request. I have a django backend and a vue3 frontend.
Here is my django view:
views.py
# Sign user in and issue token
@require_http_methods(['POST'])
def login(request: HttpRequest):
if request.method == 'POST':
# check that required fields were send with POST
print(request.body)
if {'username', 'password'} >= set(request.POST): # <- evaluates as False
print('missing required fields. INCLUDED: ' str(request.POST))
return JsonResponse(
data={'message': 'Please provide all required fields.'},
content_type='application/json',
status=400)
# check that username exists and password matches
if (User.objects.filter(username=request.POST['username']).count() >
0):
user = User.objects.get(username=request.POST['username'])
if user.password == request.POST['password']:
# Delete previously issued tokens
Token.objects.filter(user_id=user.id).delete()
token = Token(user_id=user.id)
token.save()
return JsonResponse(data={'userToken': token.to_json()},
content_type='application/json',
status=200)
else:
return JsonResponse(
data={'message': 'Incorrect username or password.'},
content_type='application/json',
status=400)
else:
return HttpResponseNotAllowed(permitted_methods=['POST'])
And my axios request
Login.vue
axios
.post('http://localhost:8000/api/users/login', {
'username': form.get('username'),
'password': form.get('password'),
}, {
validateStatus: (status) => {
return status !== 500
},
})
.then((response) => {
console.log(response.data)
if (response.data.success) {
// Commit token value to store
store.commit('setToken', response.data.token)
// Request user data ...
} else {
alert.message = response.data.message
alert.type = 'error'
document.querySelector('#alert')?.scrollIntoView()
}
})
I can see that username and password are set in request.body but not request.POST as shown in the django log.
December 01, 2021 - 17:40:29
Django version 3.2.9, using settings 'webserver.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
b'{"username":"site.admin","password":"password"}'
missing required fields. INCLUDED: <QueryDict: {}>
Bad Request: /api/users/login
[01/Dec/2021 17:40:30] "POST /api/users/login HTTP/1.1" 400 50
/Users/colby/Projects/emotions/backend/webserver/users/views.py changed, reloading.
Watching for file changes with StatReloader
Performing system checks...
What am I doing incorrectly?
Edit
Here are the headers set from the axios request
{
'Content-Length': '47',
'Content-Type': 'application/json',
'Host': 'localhost:8000',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0',
'Accept': 'application/json,
text/plain, */*',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Origin': 'http://localhost:8080',
'Dnt': '1',
'Connection': 'keep-alive',
'Referer': 'http://localhost:8080/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'cross-site',
'Sec-Gpc': '1'
}
Edit 2
I was able to correct this issue by explicitly setting the Content-Type header to multipart/form-data.
axios
.post('http://localhost:8000/api/users/login', formData,
{
validateStatus: (status) => {
return status !== 500
},
headers: {
'Content-Type': 'multipart/form-data',
}
).then(...)
I also had to adjust the if statement in the django view to
if 'username' not in request.POST or 'password' not in request.POST:
...
CodePudding user response:
I suspect Axios is setting automatically a content-type: application/json header which means that the request body doesn't contain HTTP POST parameters that can be understood by Django.
You can verify this by printing request.headers, and, if this is indeed the issue, solve it by either:
- Parsing the request body from JSON:
import json
data = json.loads(response.body.decode())
if {'username', 'password'} >= set(data): # etc.
- Send the data as form data client-side:
var formData = new FormData();
formData.append("username", form.get("username"))
formData.append("password", form.get("password"))
axios
.post('http://localhost:8000/api/users/login',
formData,
{headers: {'Content-Type': 'multipart/form-data'}
})
I'd personally recommand solution 1 because I find it less verbose and easier to maintain, but that mostly depends on your preferences and requirements :)
