Can't post data from Angular 8 to Django - django

I'm trying to send data from Angular to Django and I keep getting the following error message in my browser:
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:8000/polls/token", ok: false, …}
error:
error: SyntaxError: Unexpected token N in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:32795:51) at ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:6802:31) at Zone.runTask (http://localhost:4200/polyfills.js:6579:47) at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:6876:34) at invokeTask (http://localhost:4200/polyfills.js:8014:14) at XMLHttpRequest.globalZoneAwareCallback (http://localhost:4200/polyfills.js:8051:21)
message: "Unexpected token N in JSON at position 0"
stack: "SyntaxError: Unexpected token N in JSON at position 0↵ at JSON.parse (<anonymous>)↵ at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:32795:51)↵ at ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:6802:31)↵ at Zone.runTask (http://localhost:4200/polyfills.js:6579:47)↵ at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:6876:34)↵ at invokeTask (http://localhost:4200/polyfills.js:8014:14)↵ at XMLHttpRequest.globalZoneAwareCallback (http://localhost:4200/polyfills.js:8051:21)"
__proto__: Error
text: "None"
__proto__: Object
headers: HttpHeaders
lazyInit: () => {…}
lazyUpdate: null
normalizedNames: Map(0) {}
__proto__: Object
message: "Http failure during parsing for http://localhost:8000/polls/token"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "http://localhost:8000/polls/token"
__proto__: HttpResponseBase
I also get this error from django:
AttributeError at /polls/token
'NoneType' object has no attribute 'set_cookie'
Here is my Angular frontend:
data.service.ts:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
const httpOptions = {
withCredentials: true,
};
#Injectable({
providedIn: 'root'
})
export class DataService {
constructor(private http: HttpClient) { }
public sendPostRequest(data){
return this.http.post("//localhost:8000/polls/token", data, httpOptions);
}
}
here's an excerpt of myapp.component.ts:
...
constructor(
private dataService: DataService
) {}
...
this.auth2.attachClickHandler(
element,
{},
(googleUser) => {
let token = googleUser.getAuthResponse().id_token;
this.dataService.sendPostRequest(token).subscribe((data: any[])=>{
console.log(data);
});
},
(error) => {
alert(JSON.stringify(error, undefined, 2));
}
);
...
console.log(token) returns a long string of letters&numbers
And here's my Django backend:
polls/views.py:
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponse, HttpRequest
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def access_token(request: HttpRequest) -> HttpResponse:
if request.method == 'POST':
token = request.POST.get('body')
return HttpResponse(token)
polls/urls.py:
from django.conf import settings
from . import views
from django.urls import path
urlpatterns = [
path('token', views.access_token, name='access_token')
]
I'm new to django and angular and have been trying to figure this out for a few days now. I can't seem to find out what exactly is wrong.
Would really appreciate the help, Thank you!

Related

Axios post request to route 'appName/v1/users/' (Djoser) throws 401 error but Postman doesn't

I'm new to Django and trying to build basic user authentication with REST API and a Vue.js frontend. To send the request, I am using axios, which is configured first in a seperate composable axios.js:
import axios from 'axios'
axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://localhost:8000'
and used inside a register.vue component:
const submitRegistration = () => {
axios.post('api/v1/users/', {username: 'userName', password: 'userPassword'})
.then(res => {
console.log(res)
})
}
To make it simple, I'm sending a data-object with predefined strings, as you can see above. The request gets sent to one of the djoser routes in projectName/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('djoser.urls')),
path('api/v1/', include('djoser.urls.authtoken')),
]
This however, throws a 401 Unauthorized Error:
code: "ERR_BAD_REQUEST"
config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}
message: "Request failed with status code 401"
name: "AxiosError"
request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: true, upload: XMLHttpRequestUpload, …}
response: {data: {…}, status: 401, statusText: 'Unauthorized', headers: AxiosHeaders, config: {…}, …}
stack: "AxiosError: Request failed with status code 401\n at settle (http://localhost:3000/node_modules/.vite/deps/axios.js?v=a45c5ec0:1120:12)\n at XMLHttpRequest.onloadend (http://localhost:3000/node_modules/.vite/deps/axios.js?v=a45c5ec0:1331:7)"
I've configured settings.py like so:
INSTALLED_APPS = [
...,
'corsheaders',
'rest_framework',
'rest_framework.authtoken',
'djoser'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...,
]
CORS_ALLOWED_ORIGINS = ['http://localhost:3000']
CORS_ALLOW_CREDENTIALS = True
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
Edit:
The problem might be caused by authToken. In the root vue component app.vue, I am checking the store for an authentication token and setting an axios header accordingly:
onMounted(() => {
if (store.authToken) {
axios.defaults.headers.common['Authorization'] = `Token ${store.authToken}`
}
})
If I remove this code, the error turns into 400 BAD_REQUEST. Although I don't understand why one would need an authToken when registering a new user.
store.authToken is set during initialization:
import {reactive} from "vue";
export const store = reactive({
authToken: localStorage.authToken ?? null,
})
and shoud be null, when a new user is registered.
Edit2:
I followed some advice and used Postman to send the post request to http://localhost:8000/api/v1/users/ and it works. A new user is created.
My authentication check in the vue root component app.vue was flawed:
<script setup>
onMounted(() => {
if (store.authToken) {
axios.defaults.headers.common['Authorization'] = `Token ${store.authToken}`
}
})
</script>
store.authToken always returned true despite being null because it was fetched from localStorage being a string. JSON.parse() solved the problem.

Graphene-Django not Sending HTTPOnly JWT Cookie to React-Apollo

Hi so I've been making an app using Django and Graphene for a GraphQL server that will be accessed by a React client using Apollo. I'm using graphql_jwt for authentication.
I have set up my GraphQL server and the client code to read the JWT but I've learned that localStorage is not safe enough and neither is a cookie. I managed to find a way to set an HTTPOnly cookie when making the tokenAuth request and the cookie persists and is functional in GraphiQL (localhost:8000).
I managed to do this with jwt_cookie decorator from graphql_jwt.decorators
from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt
from graphql_jwt.decorators import jwt_cookie
urlpatterns = [
path('admin/', admin.site.urls),
path('graphql/', jwt_cookie(csrf_exempt(GraphQLView.as_view(graphiql=True))))
]
Unfortunately, when I call the tokenAuth endpoint from Apollo (localhost:3000) it is neither setting the cookie nor is can I access it from my afterware (makes sense now that I think about it). Neither the same-origin or include credentials work in the server HttpLink or the ApolloClient itself.
import {
ApolloClient,
gql,
ApolloProvider,
HttpLink,
from,
useQuery,
ApolloLink
} from '#apollo/client';
import { onError } from '#apollo/client/link/error';
import { setContext } from 'apollo-link-context';
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message }) => {
console.log(message);
});
}
if (networkError) {
console.log(networkError.message)
}
});
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const { response: { headers } } = context;
console.log(headers);
return response;
});
});
const link = from([
errorLink,
setContext((operation) => {
console.log("HITTING SET CONTEXT");
// const token = localStorage.getItem('authToken');
const token = Cookies.get('authToken');
console.log(token);
return {
headers: {
Authorization: token ? `JWT ${token}` : ''
}
}
}),
afterwareLink,
new HttpLink({ uri: 'http://localhost:8000/graphql/' })
]);
const client = new ApolloClient({
link,
cache,
typeDefs,
credentials: 'include'
});
Please ignore the refreshToken and authToken values. I set them with the js-cookie package. But as you can see the Headers are empty and weren't set on login on the client but they were on the server.
QUESTION:
How do I pass the jwt response headers to the frontend?
SO I was able to resolve this. I posted my resolution on Github
https://github.com/flavors/django-graphql-jwt/issues/191

Django CSRF Token with Axios

Situation:
I am attempting build a full SPA using Vue.js as my front end and Django as my back end. These systems are entirely separate (not a hybrid app with the index.html page served by the back end).
Approach
I created a services directory in my Vue-CLI generated project that provides the general accessibility for my REST API via the api.js file (contents below):
import axios from "axios";
import Cookies from "js-cookie";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.xsrfCookieName = "csrftoken";
const BackEnd = "http://127.0.0.1:8000/"; //local backend from manage.py runserver
export default axios.create({
baseURL: `${BackEnd}api/`,
timeout: 5000,
headers: {
"Content-Type": "application/json",
"X-CSRFToken": Cookies.get('csrftoken')
}
});
How do I know there is such a token to get? I wrote an API endpoint that provides the token in the Response headers (shown below):
Access-Control-Allow-Origin: *
Content-Length: 77
Content-Type: application/json
Date: Sun, 19 Jul 2020 18:04:06 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Set-Cookie: csrftoken=HdM4y6PPOB44cQ7DKmla7lw5hYHKVzTNG5ZZJ2PqAUWE2C79VBCJbpnTyfEdX3ke; expires=Sun, 18 Jul 2021 18:04:06 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Vary: Cookie, Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Problem
While my Django REST Framework API is doing a create job serving up all the data for my GET requests, I cannot seem to assign the csrftoken properly to authenticate my POST requests. Even with the X-CSRFToken header appropriately set in my axios request, I still get the typical 403 (CSRF cookie not set) response from the server
Request Headers
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 247
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9vOu1sBaQrXtXseR
DNT: 1
Host: 127.0.0.1:8000
Origin: http://127.0.0.1:8080
Referer: http://127.0.0.1:8080/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36
X-CSRFToken: T2Z7pzxKTAuCvBEIjkgRf8RGEEVLYfOyDYkYIcfkWCfSkPB76wCjMMizZvdTQPKg
UPDATE
Okay now this is just a pain! I've got a different token value in A) the Set-Cookie response header, B) the value for the csrftoken in my browser cookies, and C) in the axios POST request. Can anyone help me figure out what's going on here?
Django
you need youse djoser in django for authentication
wright
pip install djangorestframework-simplejwt
pip install djoser
settings.py changes
Add djoser in your INSTALLED_APPS
INSTALLED_APPS=[
...,
'djoser',
...
]
Add in your MIDDLEWERE
MIDDLEWERE=[
...,
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
]
Add
# DRF settings
REST_FRAMEWORK = {
# Default permissions
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
],
# Token types
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication"
],
}
DJOSER = {
'PASSWORD_RESET_CONFIRM_URL':
'reset_password/{uid}/{token}',
'ACTIVATION_URL': 'activation/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'SEND_CONFIRMATION_EMAIL': True,
'TOKEN_MODEL': None,
'HIDE_USERS': True,
'SERIALIZERS': {
},
'PERMISSIONS': {
'activation': ['rest_framework.permissions.AllowAny'],
'password_reset': ['rest_framework.permissions.AllowAny'],
'password_reset_confirm': ['rest_framework.permissions.AllowAny'],
'set_password': ['djoser.permissions.CurrentUserOrAdmin'],
'username_reset': ['rest_framework.permissions.AllowAny'],
'username_reset_confirm': ['rest_framework.permissions.AllowAny'],
'set_username': ['djoser.permissions.CurrentUserOrAdmin'],
'user_create': ['rest_framework.permissions.AllowAny'],
'user_delete': ['djoser.permissions.CurrentUserOrAdmin'],
'user': ['djoser.permissions.CurrentUserOrAdmin'],
'user_list': ['djoser.permissions.CurrentUserOrAdmin'],
'token_create': ['rest_framework.permissions.AllowAny'],
'token_destroy': ['rest_framework.permissions.IsAuthenticated'],
}
}
# JWT settings
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=2),
'REFRESH_TOKEN_LIFETIME': timedelta(days=5),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('JWT',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(days=2),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=5),
}
In your app urls.py add djoser urls
urlpatterns = [
# DRF router
path('', include(router.urls)),
# djoser auth urls
url(r'^auth/', include('djoser.urls')),
# djoser auth jwt urls
url(r'^auth/', include('djoser.urls.jwt')),
# Login GUI DRF
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
Vue
Base API URL
project/src/api/common.js
import axios from 'axios'
export const HTTP = axios.create({
baseURL: 'http://api-url',
})
Base element project/src/api/element.js
import {HTTP} from './common'
function createHTTP(url) {
return {
async post(config) {
return HTTP.post(`${url}`, config).then(response => {
console.log(response)
return response.data
})
},
async get(element) {
return HTTP.get(`${url}${element.id}/`)
},
async patch(element) {
console.log(element)
return HTTP.patch(`${url}${element.id}/`, element).then(response => {
console.log(response)
return response.data
})
},
async delete(id) {
HTTP.delete(`${url}${id}/`)
return id
},
async list(queryParams = '') {
return HTTP.get(`${url}${queryParams}`).then(response => {
return response.data.results
})
}
}
}
export const Todos = createHTTP(`/todos/`)
Your store project/src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import todos from "#/store/modulse/todos";
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
todos,
}
})
Your mutations types project/src/store/mutation-types.js
export const SET_TODOS ='SET_TODOS'
export const PATCH_TODO ='PATCH_TODO'
export const DELETE_TODO ='DELETE_TODO'
export const CREATE_TODO ='CREATE_TODO'
Your module project/src/store/modulse/todos.js
import {
Todos,
} from '#/api/elements'
import {
SET_TODOS, PATCH_TODO, DELETE_TODO, CREATE_TODO
} from '../mutation-types'
// Getters
export default {
state: {
todos: []
},
getters: {
getTodos(state) {
return state.todos
},
},
// Mutations
mutations: {
[SET_TODOS](state, todos) {
state.todos = todos
},
[PATCH_TODO](state, todos) {
let id = todos.id
state.todos.filter(todos => {
return todos.id === id
})[0] = todos
},
[CREATE_TODO](state, todo) {
state.todos = [todo, ...state.todos]
},
[DELETE_TODO](state, {id}) {
state.todos = state.todos.filter(todo =>{
return todo.id !==id
})
},
},
// Actions
actions: {
async setTodos({commit}, queryParams) {
await Todos.list(queryParams)
.then(todos => {
commit(SET_TODOS, todos)
}).catch((error) => {
console.log(error)
})
},
async patchTodo({commit}, todoData) {
await Todos.patch(todoData)
.then(todo => {
commit(PATCH_TODO, todo)
}).catch((error) => {
console.log(error)
})
},
async deleteTodo({commit}, todo_id) {
await Todos.delete(todo_id)
.then(resp => {
commit(DELETE_TODO, todo_id)
}).catch((error) => {
console.log(error)
})
},
async createTodo({commit}, todoData) {
await Todos.create(todoData)
.then(todo => {
commit(CREATE_TODO, todo)
}).catch((error) => {
console.log(error)
})
},
}
In your project/src/main.js
import Vue from 'vue'
import store from './store'
import App from './App.vue'
import Axios from 'axios'
Vue.prototype.$http = Axios;
new Vue({
store,
render: h => h(App),
}).$mount('#app')
In your project/src/App.vue
import {mapActions, mapGetters} from "vuex";
export default {
name: 'App',
components: {},
data() {
return {}
},
methods: {
...mapActions(['setTodos','patchTodo','createTodo','deleteTodo']),
},
computed: {
...mapGetters(['getTodos']),
},
async mounted() {
await this.setTodos()
},
}
I simply used this in my vue app, and everything worked smoothly.
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios({
method: 'post',
url: 'http://127.0.0.1:8000/api/orders-update',
xstfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFToken',
data: updateIDs,
headers: {
'X-CSRFToken': 'csrftoken',
}
}).then(response => console.log(response));

Is there some equivalent to {% url 'some-name' %} for router.register(...)

I am trying to get the URL to a specific ViewSet automatically.
I tried using {% url 'api-movement' %} but get the following error:
Reverse for 'api-movement' not found. 'api-movement' is not a valid view function or pattern name.
I searched the web but couldn't really find what I was looking for.
viewset.py
class MovementViewSet(viewsets.ModelViewSet):
queryset = Movement.objects.all()
serializer_class = MovementSerializer
router.py
from farm.api.viewsets import *
from rest_framework import routers
router = routers.DefaultRouter()
router.register('movement', MovementViewSet, base_name='api-movement')
urls.py
from django.urls import path, include
from farm.api.responder import *
from farm import views
from farm.router import router
urlpatterns = [
path('api/', include(router.urls)),
]
html excerpt:
$.ajaxSetup({
headers: { "X-CSRFToken": '{{csrf_token}}' },
});
$.ajax({
method: 'POST',
url: "{% url 'api-movement' %}",
data: {pk: pk},
success: function (data) {
location.reload();
},
error: function (error_data) {
alert('An error occurred, please reload page and try again.\n\nError: ' + error_data.responseText);
}
});

Angular 4 HTTP requests to Django Rest Framework

I'm having issues finding the answer anywhere to an issue I'm having related to the (I think) Authorization header in an HTTP request I'm sending from Angular 4 to the Django Rest Framework API I've created. Lets get down to it:
EDIT:
Confirmed that the problem relates to authorization since I am also using django-cors-headers now to rid myself of CORS issues for the 4200 port (since it is considered a different origin). The problem that remains is simply that I get the message "Unauthorized" when making requests towards the API. I'm starting to think it is an encoding issue since the following message is shown when I attempt the following request with httpie:
http GET http://localhost:8000/api/groups/ "Authorization: Basic admin:password"
And then the message is shown:
{
"detail": "Invalid basic header. Credentials not correctly base64 encoded."
}
In settings.py, I've made sure that both a permission class and authentication class have been made available.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAdminUser'
],
...
CORS_ORIGIN_WHITELIST = [
'localhost:4200',
]
In views.py, this is perhaps where my fault is since the console error I get when trying to send the request from Angular -> Rest API is that 'No 'Access-Control-Allow-Origin' header is present on the requested resource.' which is untrue since it does NOT complain about this when I remove the authentication need.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = (IsAdminUser, )
authentication_classes = (BasicAuthentication, )
def list(self, request):
queryset = User.objects.all().order_by('-date_joined')
serializer = UserSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data,
status=200)
The Angular dev server is running on localhost:4200 while django is left on its default of localhost:8000.
I'll include the urls.py for good measure:
from django.conf.urls import url, include
from rest_framework import routers
from backend.api import views
from django.contrib import admin
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
router.register(r'info', views.InfoViewSet)
urlpatterns = [
url(r'^api/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^admin/', admin.site.urls),
]
NOTE that I can make the request and get a response just fine with httpie using the authorization header like so:
http GET http://localhost:8000/api/users/ -a admin:password
Finally, here is the Angular code for making the request (I included everything so that imports can be checked as well):
import { Component, OnInit } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
results: string[];
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get(
'http://localhost:8000/api/users/',
{ headers: new HttpHeaders().set('Authorization', 'Basic admin:password'), }
).subscribe(
data => {
this.results = data;
},
err => {
console.log("ERROR RETREIVING USERS");
});
}
}
I have also imported HttpClientModule and listed it under 'imports' in my app.module.
Separate ports are considered different origins, so you will of course, get a Cross-Origin error (CORS) on localhost. This is a browser security feature alongwith your server.
Instal django-cross-header package for resolving cross-domain error.
I use the same Backend Framework , and the solution I've found was to build my project with the right host using
ng build --production -host=myDomain.com // try localhost -p 8080 in your case
and replace
Access-Control-Allow-Origin: 'http://localhost:4200'
with
Access-Control-Allow-Origin: '*'
The problem was solved in two ways. Firstly, I did not correctly ensure that CORS would be enabled from the origin of where Angular 4 was sending the request. #kmcodes solution of using django-cors-headers was a good one and I no longer had an issue with 'Access-Control-Allow-Origin' being missing after adding it to my project.
The second part of the problem was in the header I was putting on to the request sent by Angular 4. The following was the change I needed to make:
this.http.get(
'http://localhost:8000/api/users/',
{ headers: new HttpHeaders().set('Authorization', 'Basic admin:password'), }
).subscribe(
data => {
this.results = data;
},
err => {
console.log("ERROR RETREIVING USERS");
});
To this:
this.http.get(
'http://localhost:8000/api/users/',
{ headers: new HttpHeaders().set('Authorization', 'Basic ' + btoa('admin:password')), }
).subscribe(
data => {
this.results = data;
},
err => {
console.log("ERROR RETREIVING USERS");
});
I got the hint when using httpie to inspect my request and saw that when I didn't use the -a flag to add authentication parameters but rather the 'Authorization' header. This gave me an error stating that the request failed with error code 401, unauthorized, since the username:password part was not encoded with base64. In Angular 4 (and maybe before) btoa() solves this as above.