CORS Headers – komunikacja VueJS i Django

vuejs logoCross-Origin Resource Sharing (CORS) to mechanizm wykorzystujący dodatkowe nagłówki HTTP pozwalające różnym agentom (np. przeglądarkom) na dostęp do zasobów znajdujących się na innej domenie, porcie czy protokole niż aktualna. 

Na potrzeby wpisu zakładam, że framework (Django) i biblioteki (Vue.js i axios) z których będziemy korzystać – mamy już zainstalowane w projekcie.

W projekcie Vue wprowadźmy następujące zmiany:

W pliku App.vue:


<template>
<div id="app">
<h1>IMDb Top Rated Movies</h1>
<ul>
<li v-for="movie in movies">
{{ movie.title }} <em>{{ movie.year }}</em>
</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data: function() {
return {
movies: []
};
},
created() {
axios
.get(process.env.API_URL)
.then(results => {
this.movies = results.data;
})
.catch(error => {
console.log(error);
});
}
};
</script>
<style>
#app {
fontfamily: "Avenir", Helvetica, Arial, sansserif;
webkitfontsmoothing: antialiased;
mozosxfontsmoothing: grayscale;
color: #2c3e50;
margintop: 60px;
}
</style>

view raw

App.js

hosted with ❤ by GitHub

W pliku config/dev.env.js:


'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API_URL: '"//127.0.0.1:8000/"'
})

view raw

dev.env.js

hosted with ❤ by GitHub

Pliki src/assets/logo.png i src/components/HelloWorld.vue możemy wyrzucić, jako w tej chwili nie używane.

W backendzie natomiast, w pliku views.py naszej aplikacji (movies):


from django.http import JsonResponse
def top_rated(request):
return JsonResponse(
[
{
'title': 'Skazani na Shawshank',
'year': 1994
},
{
'title': 'Ojciec chrzestny',
'year': 1972
},
{
'title': 'Ojciec chrzestny II',
'year': 1974
},
{
'title': 'Mroczny Rycerz',
'year': 2008
},
{
'title': 'Dwunastu gniewnych ludzi',
'year': 1957
}
],
safe=False
)

view raw

views.py

hosted with ❤ by GitHub

i dodajemy routing dla naszej aplikacji w pliku urls.py (plik ten domyślnie nie istnieje i należy go utworzyć):


# movies/urls.py
from django.conf.urls import include, url
from .views import top_rated
urlpatterns = [
url(r'^$', top_rated, name='top-rated'),
]

view raw

urls.py

hosted with ❤ by GitHub

Następnie dokunujemy zmian w plikach urls.py – głównym pliku routingu całego projektu Django:


# backend/urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^', include('movies.urls')),
url(r'^admin/', admin.site.urls),
]

view raw

urls.py

hosted with ❤ by GitHub

oraz settings.py:


INSTALLED_APPS = [
# …
'django.contrib.staticfiles',
'movies'
]

view raw

settings.py

hosted with ❤ by GitHub

Startujemy serwery poniższymi poleceniami w odpowiednich katalogach:


# run Vue.js 2 development server (default port 8080)
npm run dev
# run Django development server (default port 8000)
python manage.py runserver

view raw

runservers.sh

hosted with ❤ by GitHub

Uruchamiamy przeglądarkę i otwieramy 2 zakładki. W pierwszej zweryfikujemy tylko, czy serwer Django zwraca prawidłową odpowiedź – JSON (http://127.0.0.1:8000) i jeśli tak, to od razu można ją zamknąć. W drugiej zakładce otwieramy narzędzia programisty i przechodzimy pod adres aplikacji Vue (http://127.0.0.1:8080).
Dziwne – w zakładce Network zobaczyć możemy, że żądanie GET do serwera się udało i nawet możemy podejrzeć zwrócone wyniki:


[
{
"title": "Skazani na Shawshank",
"year": 1994
},
{
"title": "Ojciec chrzestny",
"year": 1972
},
{
"title": "Ojciec chrzestny II",
"year": 1974
},
{
"title": "Mroczny Rycerz",
"year": 2008
},
{
"title": "Dwunastu gniewnych ludzi",
"year": 1957
}
]

Czyli wszystko jest prawidłowo? No nie do końca niestety. O ile domena (host) – w tej sytuacji 127.0.0.1 nie są problemem – to sprawa komplikuje się właśnie w przypadku niezgodności portów i przeglądarka może nam w konsoli sypnąć następującym błędem:


Failed to load http://127.0.0.1:8000/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8080&#39; is therefore not allowed access.

Problem nasz rozwiązać możemy paczką django-cors-headers, jej instalacja nie odbiega za bardzo od instalacji innych paczek Pythona:

pip install django-cors-headers==2.1.0

Zmieniamy trochę w pliku settings.py (ale naprawdę ciut-ciut):


# Zmieniamy
INSTALLED_APPS = [
# …
'django.contrib.staticfiles',
'corsheaders',
'movies'
]
# Zmieniamy
MIDDLEWARE = [
# …
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# …
]
# A to coś nowego
CORS_ORIGIN_WHITELIST = (
'127.0.0.1:8080'
)

view raw

settings.py

hosted with ❤ by GitHub

… restartujemy serwer Django i już, wchodząc na adres naszej aplikacji powinniśmy cieszyć się pobranymi z serwera danymi.

Wymagane paczki do postawienia projektu Django znajdują się  w katalogu backend/requirements/base.txt, natomiast całość kodu źródłowego bezpośrednio w repozytorium VueJS-Django-CORS.

Dla zainteresowanych można jeszcze poszukać informacji na temat Same Origin Policy.

Dodatkowe źródła (często z fajnymi obrazkami):

  1. MDN CORS
  2. Understanding CORS
  3. Understanding CORS: Cross-Origin Resource Sharing
  4. Understanding and using CORS

 

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s

Ta witryna wykorzystuje usługę Akismet aby zredukować ilość spamu. Dowiedz się w jaki sposób dane w twoich komentarzach są przetwarzane.