Appearance
Kapitel 37: axios (Teil 2) - Instanz & Interceptors
📙 Lernziel: axios Instanz erstellen & Interceptors meistern!
37.1 Was ist eine axios Instanz?
axios Instanz erlaubt wiederverwendbare Konfigurationen.
Vorteile:
- ✅ Basis-URL festlegen (
baseURL) - ✅ Standard-Header setzen
- ✅ Timeout konfigurieren
- ✅ Interceptors hinzufügen
Beispiel:
javascript
// utils/axiosInstance.js
import axios from 'axios'
const apiClient = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
})
export default apiClient37.2 axios Instanz verwenden
Beispiel:
vue
<!-- views/PostsView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import apiClient from '../utils/axiosInstance.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()">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;
margin-top: 20px;
}
</style>37.3 Request Interceptor
Request Interceptor wird vor jedem Request ausgeführt.
Anwendungsfälle:
- ✅ Token hinzufügen (Authorization Header)
- ✅ Loading-State aktivieren
- ✅ Request-Logging
Beispiel:
javascript
// utils/axiosInstance.js
import axios from 'axios'
const apiClient = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com'
})
// Request Interceptor
apiClient.interceptors.request.use(
(config) => {
// Token aus localStorage hinzufügen
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
console.log('Request:', config.method.toUpperCase(), config.url)
return config
},
(error) => {
return Promise.reject(error)
}
)
export default apiClient37.4 Response Interceptor
Response Interceptor wird nach jedem Response ausgeführt.
Anwendungsfälle:
- ✅ Token-Refresh bei 401 Fehler
- ✅ Globales Error-Handling
- ✅ Response-Transformation
Beispiel:
javascript
// utils/axiosInstance.js
import axios from 'axios'
import { useRouter } from 'vue-router'
const apiClient = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com'
})
// Response Interceptor
apiClient.interceptors.response.use(
(response) => {
console.log('Response:', response.status, response.config.url)
return response
},
async (error) => {
const router = useRouter()
if (error.response) {
// Server antwortet mit Fehler-Status
if (error.response.status === 401) {
// Token abgelaufen → Redirect zu Login
localStorage.removeItem('token')
router.push('/login')
} else if (error.response.status === 403) {
// Keine Berechtigung
alert('Keine Berechtigung!')
} else if (error.response.status === 500) {
// Server-Fehler
alert('Server-Fehler! Bitte später erneut versuchen.')
}
} else if (error.request) {
// Keine Antwort vom Server
alert('Keine Verbindung zum Server!')
} else {
// Fehler beim Request-Setup
console.error('Request Fehler:', error.message)
}
return Promise.reject(error)
}
)
export default apiClient37.5 Vollständiges Beispiel (axios Instanz + Interceptors)
utils/apiClient.js:
javascript
// utils/apiClient.js
import axios from 'axios'
import { useRouter } from 'vue-router'
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// Request Interceptor
apiClient.interceptors.request.use(
(config) => {
// Token hinzufügen
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// Loading-State (optional)
// store.dispatch('setLoading', true)
return config
},
(error) => {
return Promise.reject(error)
}
)
// Response Interceptor
apiClient.interceptors.response.use(
(response) => {
// Loading-State beenden (optional)
// store.dispatch('setLoading', false)
return response
},
async (error) => {
const router = useRouter()
if (error.response) {
const { status } = error.response
if (status === 401) {
// Token abgelaufen → Refresh Token versuchen
const refreshToken = localStorage.getItem('refreshToken')
if (refreshToken) {
try {
const response = await axios.post('https://api.example.com/auth/refresh', {
refreshToken
})
const { token } = response.data
localStorage.setItem('token', token)
// Original Request mit neuem Token wiederholen
error.config.headers.Authorization = `Bearer ${token}`
return apiClient(error.config)
} catch (refreshError) {
// Refresh Token auch abgelaufen → Logout
localStorage.removeItem('token')
localStorage.removeItem('refreshToken')
router.push('/login')
}
} else {
router.push('/login')
}
} else if (status === 403) {
alert('Keine Berechtigung!')
} else if (status === 500) {
alert('Server-Fehler!')
}
} else if (error.request) {
alert('Keine Verbindung zum Server!')
}
return Promise.reject(error)
}
)
export default apiClientVerwendung:
vue
<!-- views/ProfileView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import apiClient from '../utils/apiClient.js'
const user = ref(null)
const isLoading = ref(false)
const fetchUserProfile = async () => {
isLoading.value = true
try {
const response = await apiClient.get('/user/profile')
user.value = response.data
} catch (error) {
console.error('Fehler:', error)
} finally {
isLoading.value = false
}
}
onMounted(fetchUserProfile)
</script>
<template>
<div class="profile-view">
<h2>Profil</h2>
<div v-if="isLoading">Lädt...</div>
<div v-else-if="user">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
<p>Stadt: {{ user.city }}</p>
</div>
<div v-else>
<p>Keine Daten gefunden.</p>
</div>
</div>
</template>37.6 Übung: API Client mit Interceptors
Aufgabe: Erstelle einen API Client mit Request & Response Interceptors.
Lösung:
1. API Client erstellen (utils/api.js):
javascript
// utils/api.js
import axios from 'axios'
const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 5000
})
// Request Interceptor (Token hinzufügen)
api.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)
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default api2. Verwendung in Komponente:
vue
<!-- views/TodosView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import api from '../utils/api.js'
const todos = ref([])
const isLoading = ref(false)
const fetchTodos = async () => {
isLoading.value = true
try {
const response = await api.get('/todos?_limit=5')
todos.value = response.data
} catch (error) {
console.error('Fehler:', error)
} finally {
isLoading.value = false
}
}
onMounted(fetchTodos)
</script>
<template>
<div>
<h2>Todos</h2>
<div v-if="isLoading">Lädt...</div>
<ul v-else>
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" :checked="todo.completed" />
<span :style="{ textDecoration: todo.completed ? 'line-through' : 'none' }">
{{ todo.title }}
</span>
</li>
</ul>
</div>
</template>✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was eine axios Instanz ist
- ✅ axios Instanz erstellen & verwenden
- ✅ Request Interceptor (Token hinzufügen)
- ✅ Response Interceptor (Fehlerbehandlung)
- ✅ Vollständiges Beispiel (Token Refresh)
- ✅ Praxis: API Client mit Interceptors
🎯 Nächster Schritt: In Kapitel 38 lernst du CORS (Cross-Origin Resource Sharing)!
← Zurück zu Kapitel 36: axios GrundlagenWeiter zu Kapitel 38: CORS →
