Appearance
Kapitel 38: axios (Teil 3) - CORS & Fehlerbehandlung
📙 Lernziel: CORS verstehen und lösen!
38.1 Was ist CORS?
CORS (Cross-Origin Resource Sharing) ist eine Sicherheitsrichtlinie von Browsern.
Problem:
Frontend: http://localhost:5173
Backend: http://api.example.com:3000
↑
Anderer Origin = CORS Fehler!Fehlermeldung:
Access to XMLHttpRequest at 'http://api.example.com'
from origin 'http://localhost:5173' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.38.2 CORS lösen (Backend)
Lösung 1: Backend konfigurieren (Empfohlen!)
Node.js (Express):
javascript
// server.js
const express = require('express')
const app = express()
// CORS Middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:5173')
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
next()
})
// Oder mit cors Bibliothek
const cors = require('cors')
app.use(cors({
origin: 'http://localhost:5173',
credentials: true
}))
app.listen(3000, () => {
console.log('Server läuft auf Port 3000')
})Spring Boot (Java):
java
// CorsConfig.java
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:5173");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}38.3 CORS lösen (Frontend - Proxy)
Lösung 2: Proxy in Vite konfigurieren (Entwicklungsumgebung!)
Vite Konfiguration (vite.config.js):
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
// API Anfragen proxyen
'/api': {
target: 'http://api.example.com:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})Verwendung:
vue
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const data = ref(null)
onMounted(async () => {
// Statt: http://api.example.com:3000/users
// Nutze: /api/users (wird zu localhost:5173/api/users)
const response = await axios.get('/api/users')
data.value = response.data
})
</script>
<template>
<div>
<h2>Benutzer</h2>
<ul>
<li v-for="user in data" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>38.4 CORS lösen (JSONP - Veraltet!)
Lösung 3: JSONP (nur für GET, veraltet!)
vue
<script setup>
import { ref, onMounted } from 'vue'
const data = ref(null)
onMounted(() => {
// JSONP - Funktioniert nur mit GET
const script = document.createElement('script')
script.src = 'http://api.example.com:3000/users?callback=handleData'
document.body.appendChild(script)
// Global Funktion
window.handleData = (response) => {
data.value = response
}
})
</script>Nachteile:
- ❌ Nur GET Requests
- ❌ Sicherheitsrisiken (XSS)
- ❌ Veraltet (nutze lieber CORS oder Proxy)
38.5 Fehlerbehandlung (Global)
Globales Fehlerhandling mit axios Interceptors:
javascript
// utils/apiClient.js
import axios from 'axios'
import { useRouter } from 'vue-router'
const apiClient = axios.create({
baseURL: 'http://localhost:3000',
timeout: 5000
})
// Response Interceptor (Fehlerbehandlung)
apiClient.interceptors.response.use(
(response) => response,
(error) => {
const router = useRouter()
if (error.response) {
// Server hat geantwortet, aber mit Fehler-Status
const { status } = error.response
if (status === 401) {
// Unauthorized - Token abgelaufen
localStorage.removeItem('token')
router.push('/login')
} else if (status === 403) {
// Forbidden - Keine Berechtigung
alert('Keine Berechtigung für diese Aktion!')
} else if (status === 404) {
// Not Found
router.push('/404')
} else if (status === 500) {
// Serverfehler
alert('Serverfehler! Bitte später erneut versuchen.')
}
} else if (error.request) {
// Keine Antwort vom Server (Netzwerkfehler)
alert('Keine Verbindung zum Server! Bitte Internetverbindung prüfen.')
} else {
// Fehler beim Request-Setup
console.error('Request Fehler:', error.message)
}
return Promise.reject(error)
}
)
export default apiClient38.6 Übung: API Client mit Fehlerbehandlung
Aufgabe: Erstelle einen API Client mit CORS-Proxy und globalem Fehlerhandling.
Lösung:
1. Vite Konfiguration (vite.config.js):
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})2. API Client erstellen (utils/api.js):
javascript
// utils/api.js
import axios from 'axios'
import { useRouter } from 'vue-router'
const apiClient = axios.create({
baseURL: '/api', // Nutzt Vite Proxy!
timeout: 5000
})
// Request Interceptor (Token hinzufügen)
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// Response Interceptor (Fehlerbehandlung)
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
const { status } = error.response
if (status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
} else if (error.request) {
alert('Keine Verbindung zum Server!')
}
return Promise.reject(error)
}
)
export default apiClient3. Verwendung in Komponente:
vue
<!-- views/PostsView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import apiClient from '../utils/api.js'
const posts = ref([])
const isLoading = ref(false)
const error = ref(null)
const fetchPosts = async () => {
isLoading.value = true
error.value = null
try {
const response = await apiClient.get('/posts?_limit=5')
posts.value = response.data
} catch (err) {
error.value = err.message
} finally {
isLoading.value = false
}
}
onMounted(fetchPosts)
</script>
<template>
<div class="posts-view">
<h2>Posts</h2>
<div v-if="isLoading">Lädt...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<ul v-else>
<li v-for="post in posts" :key="post.id">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
</li>
</ul>
<button @click="fetchPosts()" :disabled="isLoading">
{{ isLoading ? 'Lädt...' : 'Neu laden' }}
</button>
</div>
</template>
<style scoped>
.posts-view {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.error {
color: red;
padding: 10px;
background: #ffebee;
border-radius: 4px;
}
button {
padding: 10px 20px;
background: #42b883;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
</style>✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was CORS ist (Cross-Origin Resource Sharing)
- ✅ CORS lösen (Backend konfigurieren)
- ✅ CORS lösen (Frontend Proxy mit Vite)
- ✅ CORS lösen (JSONP - veraltet!)
- ✅ Globales Fehlerhandling mit Interceptors
- ✅ Praxis: API Client mit Fehlerbehandlung
🎯 Nächster Schritt: In Kapitel 39 lernst du fortgeschrittene Vue 3 Konzepte!
← Zurück zu Kapitel 37: axios Instanz & InterceptorsWeiter zu Kapitel 39: Fortgeschrittene Konzepte →
