如何在Nuxt服务器api中重定向到指定路由?

问题描述 投票:0回答:1

我目前正在使用 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();
}

我尝试过的事情

  1. 使用useRouter().push('/')
  2. 使用重定向('/')

两种方法都会导致相同的错误。

当我在 Axios 拦截器中使用 useRouter().push('/') 时,收到错误 Vue 应用程序别名在服务器运行时不允许。

在 Axios 拦截器中收到 Unauthorized: Invalid or expired token 错误时,如何正确重定向到

/
?我应该考虑解决方法或其他方法吗?

axios nuxt.js interceptor nuxt3.js
1个回答
0
投票

TLDR; 您不能在服务器文件夹中使用特定于 Nuxt 的任何内容。请参阅

unjs/nitro
文档。要回答你的原因,你应该在你的偶数处理程序中
return sendRedirect('/')

如果您想使用

Axios
,请用承诺和
resolve
reject
括起来。然后,在 eventHandler 中捕获拒绝并
return sendRedirect()

最后,如果你想在Nuxt生态中快乐地生活,请使用

ofetch
,即Nuxt官方提供的
$fetch()

© www.soinside.com 2019 - 2024. All rights reserved.