我目前正在使用 Nuxt 做一个项目,并且我正在使用 Nuxt server/api 文件夹来存储我的所有外部 API。我想做的是,如果有任何 API 返回错误
/
,我想重定向回 Unauthorized: Invalid or expired token
页面。我为此使用 axios 拦截器,并且能够在任何 API 返回该错误时进行控制台输出。只是我无法重定向到 /
页面,因为如果我使用 useRouter().push('/');
或 redirect('/')
之类的东西,我的项目控制台将返回错误 Vue app aliases are not allowed in server runtime.
我该如何解决这个问题或者有其他解决方法吗? 我的代码如下;
-- 服务器/api/apiService.ts
import axios from 'axios';
import packageJson from '~/package.json';
import { useRouter } from 'nuxt/app';
const config = useRuntimeConfig();
// ---------- dynamic
interface EnvironmentConfigs {
[key: string]: string;
}
const environmentConfigs: EnvironmentConfigs = {
development: config.developmentBaseurl,
staging: config.stagingBaseurl,
production: config.productionBaseurl
};
const baseURL = environmentConfigs[packageJson.environment_config];
if (!baseURL) {
throw new Error('Invalid environment configuration');
}
export const devApiClient = axios.create({
baseURL,
})
// ---------------------------------------------------------------
// Add response interceptor to handle token expiration
devApiClient.interceptors.response.use(
(response) => {
// Do something with response data
return response;
},
(error) => {
// Do something with response error
if (error.response.data === 'Unauthorized: Invalid or expired token\n') {
console.error('Token expired, redirecting to login page...');
const router = useRouter();
router.push('/login');
}
return Promise.reject(error);
}
);
-- 服务器/api/apiEventHandler.ts
import { apiErrorHandler } from './apiErrorHandler';
import packageJson from '~/package.json';
const API_VERSION = packageJson.version;
type AuthType = 'Bearer' | 'Basic';
type DataFormat = 'URLSearchParams' | 'FormData';
export function apiEventHandler(handlerFn: (
headers: any, params: any, type: any) => Promise<any>,
authType: AuthType = 'Bearer', // Default to 'Bearer'
dataFormat: DataFormat = 'URLSearchParams' // Default to URLSearchParams
) {
return eventHandler(async (event: any) => {
try {
let query, token;
if (dataFormat === 'FormData') {
query = await readMultipartFormData(event);
token = query?.find((part: any) => part.name === 'token')?.data.toString();
} else {
query = JSON.parse(await readBody(event));
token = query?.token;
}
// Initialize headers based on authType
const headers: any = authType === 'Bearer'
? { 'Authorization': `Bearer ${token}` }
: { 'Authorization': `Basic ${btoa(`${useRuntimeConfig().ssoUsername}:${useRuntimeConfig().ssoPassword}`)}` };
// Initialize params based on dataFormat
const params: any = dataFormat === 'URLSearchParams'
? new URLSearchParams()
: new FormData();
// const params = new URLSearchParams();
params.append('version', API_VERSION);
// Check if query.data exists to handle params construction
if (query.data) {
for (const item of query.data) {
// Dynamically append parameters based on item keys
for (const key in item) {
if (Object.prototype.hasOwnProperty.call(item, key)) {
params.append(key, item[key]);
}
}
}
}
return await handlerFn(headers, params, query);
} catch (error: any) {
return apiErrorHandler(error);
}
});
}
-- 服务器/api/apiErrorHandler.ts
export function apiErrorHandler(error: any) {
// return { error: error.message || 'Unknown error' };
// Check if the error response is available
if (error.response) {
const { data } = error.response;
// Check if there is a specific error message
if (data && typeof data === 'string') {
return { error: data };
}
// Fallback, return the error response to the client
return {
// error: data.error.details.reason || data.error || 'An error occurred while processing the request'
error: data.error || 'An error occurred while processing the request'
};
} else {
// If no error response, return a generic error message
return {
error: error.message || 'An internal server error occurred'
};
}
}
-- 服务器/api/apiEndpoints.ts
// list of API Endpoints
export const API_ENDPOINT = {
LOGIN: 'EXTERNAL_API_ENDPOINT_HERE'
};
-- 可组合项/useApiPath.ts
export const useApiPaths = () => {
const config = useRuntimeConfig();
const baseURL = config.app.baseURL;
const apiPaths = {
apiLogin: `${baseURL}api/auth/apiLogin`
};
// console.log('Config:', config);
// console.log('API Paths:', apiPaths);
return apiPaths;
};
-- 服务器/api/auth/apiLogin.ts
import { devApiClient } from '../apiService'
import { API_ENDPOINT } from '../apiEndpoints';
import { apiEventHandler } from '../apiEventHandler';
const handlerFn = async (headers: any, params: any) => {
// Add specific headers for this API call
headers['Content-Type'] = 'application/x-www-form-urlencoded';
try {
const response = await devApiClient.post(API_ENDPOINT.LOGIN, params, { headers });
const responseData = response.data;
if (responseData.success) {
return responseData.data;
}
throw new Error('API returned error: ' + responseData.message);
} catch (error: any) {
throw error; // Allow the global error handler to handle this
}
}
export default apiEventHandler(handlerFn);
我如何在页面中调用API:
const getLogin = async () => {
const dataArray = [{ contact: emailField, code: passwordField }];
const response = await fetch(useApiPaths().apiLogin, {
method: 'POST',
body: JSON.stringify({
token: authStore.sso_token, data: dataArray
}),
});
const responseData = await response.json();
}
我尝试过的事情
两种方法都会导致相同的错误。
当我在 Axios 拦截器中使用 useRouter().push('/') 时,收到错误 Vue 应用程序别名在服务器运行时不允许。
在 Axios 拦截器中收到 Unauthorized: Invalid or expired token 错误时,如何正确重定向到
/
?我应该考虑解决方法或其他方法吗?
TLDR; 您不能在服务器文件夹中使用特定于 Nuxt 的任何内容。请参阅
unjs/nitro
文档。要回答你的原因,你应该在你的偶数处理程序中return sendRedirect('/')
。
如果您想使用
Axios
,请用承诺和 resolve
或 reject
括起来。然后,在 eventHandler 中捕获拒绝并 return sendRedirect()
最后,如果你想在Nuxt生态中快乐地生活,请使用
ofetch
,即Nuxt官方提供的$fetch()
。