Next.js部署Azure静态Web应用后,getServerSideProps引发500错误求助
问题:Next.js部署Azure静态Web应用后出现500内部服务器错误
我的Next.js应用本地运行正常,但部署到Azure静态Web应用后触发500内部服务器错误。这是首次在Azure部署Next.js应用,怀疑是配置缺失或代码问题,问题应该出在getServerSideProps方法——该方法调用公共API获取JWT令牌用于后续请求。
已尝试的操作
- 最初怀疑环境变量未加载:
- 在
getCplApiToken中硬编码process.env.CPL_API_USER_PROD和process.env.CPL_API_PSWRD_PROD,仍出现500错误; - 用
getStaticProps返回process.env.CPL_API_USER_PROD并在控制台输出,本地和Azure均能获取正确值。
- 在
- 所有环境变量已保存为GitHub Secrets,名称与本地
.env.local一致。 - 注释页面中的
getServerSideProps代码后,Azure上的500错误消失。
相关代码
页面组件代码
import { useEffect } from 'react' import { useRouter } from 'next/router' import { Box, Button, Typography } from '@mui/material' import TextFieldCustom from '@/components/TextField' import FormLayout from '@/components/FormLayout' import useCustomForm from '@/hooks/useCustomForm' import { yupResolver } from '@hookform/resolvers/yup' import {schema} from '@/lib/frm1-schema' import {defaultValues} from '@/lib/frm1-defaultValues' import getCplApiToken from '@/api/getCplApiToken' import usePasswordManagementStore from '@/store/passwordManagementStore' export async function getServerSideProps() { const resObj = await getCplApiToken() return { props: { serverProps: {status: resObj.status, token: resObj.data[0].token} }, // will be passed to the page component as props } } const Form = ({ serverProps }) => { const { bearerToken, setBearerToken, setAccountData } = usePasswordManagementStore() const router = useRouter() const { control, formState: { errors }, handleSubmit, watch } = useCustomForm({ mode: 'all', defaultValues: defaultValues, shouldUnregister: true, resolver: yupResolver(schema) }) const formData = watch() const isEmptyFieldPresent = Object.values(formData).some( (value) => value === '' || value === null) useEffect(() => { if (!bearerToken) { setBearerToken(serverProps.token) } }, [bearerToken, setBearerToken]) const submit = data => { setAccountData(data) router.push('/password-management/account-validation') } return ( <FormLayout> <section> <Typography variant='h2' component='h1' gutterBottom>PIN Reset</Typography> <Typography variant='h4' component='h2' gutterBottom>Reset your PIN securely</Typography> <Typography variant='body1' gutterBottom> Receive a validation code via email or SMS to the associated contact information on your library account. </Typography> <Box my={2}> <Typography variant='body2'>Need help? Contact library staff.</Typography> </Box> <form noValidate autoComplete='off' onSubmit={handleSubmit(submit)}> <TextFieldCustom required fullWidth name='cardNumber' label='Library card number' control={control} error={!!errors.cardNumber} helperText={errors?.cardNumber?.message} /> <TextFieldCustom required fullWidth name='Name' label='Name' control={control} error={!!errors.Name} helperText={errors?.Name?.message} /> <TextFieldCustom required fullWidth name='Lastname' label='Lastname' control={control} error={!!errors.Lastname} helperText={errors?.Lastname?.message} /> <Box sx={{ mt: 2, display: 'flex', flexDirection: 'row', justifyContent: 'flex-end' }}> <Button color='secondary' size='large' variant='contained' type='submit' disabled={isEmptyFieldPresent} > Next </Button> </Box> </form> </section> </FormLayout> ) } export default Form
getCplApiToken方法代码
import fetch from 'node-fetch' const myHeaders = {'Content-Type': 'application/json'} const getCplApiToken = async () => { let ret = null let response = null let data = null const controller = new AbortController() const raw = JSON.stringify({ username: process.env.CPL_API_USER_PROD, password: process.env.CPL_API_PSWRD_PROD }) const requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow', } try { response = await fetch(`${process.env.NEXT_PUBLIC_CPL_API_URL_PROD}/Auth`, requestOptions) // setTimeout(id) if (!response.ok) { const resError = await response.text() throw resError } data = await response.text() ret = JSON.parse(data) } catch (error) { // ret = JSON.parse(error) console.log(error) } return { status: response.status, data: ret } } export default getCplApiToken
GitHub Actions配置YML文件
on: push: branches: - staging pull_request: types: [opened, synchronize, reopened, closed] branches: - staging jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 env: CPL_API_USER_PROD: ${{ secrets.CPL_API_USER_PROD }} CPL_API_PSWRD_PROD: ${{ secrets.CPL_API_PSWRD_PROD }} CPL_API_URL_PROD: ${{ secrets.CPL_API_URL_PROD }} NEXT_PUBLIC_CPL_API_URL_PROD: ${{ secrets.CPL_API_URL_PROD }} with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_FIELD_09B90541E }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) action: "upload" ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig app_location: "/" # App source code path api_location: "" # Api source code path - optional output_location: "" # Built app content directory - optional app_build_command: npm run build api_build_command: 'rm -rf ./node_modules/@next/swc-* && rm -rf ./.next/cache' ###### End of Repository/Build Configurations ###### close_pull_request_job: if: github.event_name == 'pull_request' && github.event.action == 'closed' runs-on: ubuntu-latest name: Close Pull Request Job steps: - name: Close Pull Request id: closepullrequest uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_FIELD_09B90541E }} action: "close"
排查与解决方案
1. 修复未处理的异常导致的崩溃
当前代码存在两个核心异常点:
getCplApiToken中请求失败时,response可能未被赋值,返回response.status会直接抛出异常getServerSideProps中直接访问resObj.data[0].token,如果API返回空数据会触发解构错误
修改getCplApiToken的错误处理:
import fetch from 'node-fetch' const myHeaders = {'Content-Type': 'application/json'} const getCplApiToken = async () => { let ret = null let response = null let data = null const raw = JSON.stringify({ username: process.env.CPL_API_USER_PROD, password: process.env.CPL_API_PSWRD_PROD }) const requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow', } try { response = await fetch(`${process.env.NEXT_PUBLIC_CPL_API_URL_PROD}/Auth`, requestOptions) if (!response.ok) { const resError = await response.text() throw new Error(`API请求失败: ${resError} (状态码: ${response.status})`) } data = await response.text() ret = JSON.parse(data) } catch (error) { console.error('获取令牌失败:', error) // 返回降级数据,避免后续报错 return { status: response?.status || 500, data: [] } } return { status: response.status, data: ret } } export default getCplApiToken
修改getServerSideProps增加异常捕获和可选链:
export async function getServerSideProps() { try { const resObj = await getCplApiToken() return { props: { serverProps: { status: resObj.status, token: resObj.data?.[0]?.token || null } }, } } catch (error) { console.error('getServerSideProps执行失败:', error) // 返回降级props,防止页面崩溃 return { props: { serverProps: { status: 500, token: null } }, } } }
2. 配置Next.js适配Azure运行环境
Azure静态Web应用对Next.js的SSR支持需要启用standalone模式,减少依赖体积并适配云环境:
创建或修改next.config.js:
/** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', } module.exports = nextConfig
3. 确认运行时环境变量配置
getServerSideProps是运行时执行的,需确保Azure静态Web应用的应用设置中已配置相关变量:
- 登录Azure门户,进入目标静态Web应用
- 进入「配置」→「应用设置」
- 添加
CPL_API_USER_PROD、CPL_API_PSWRD_PROD、NEXT_PUBLIC_CPL_API_URL_PROD,值与GitHub Secrets一致 - 保存后重启应用
4. 排查API网络连通性
Azure静态Web应用的出站IP可能被目标API拦截:
- 在Azure门户的静态Web应用「概述」中获取出站IP列表,确认目标API已允许这些IP访问
- 可在代码中临时添加日志,输出API请求的完整URL和响应内容,排查请求是否正常发送
内容的提问来源于stack exchange,提问作者aadlc
相关产品推荐
相关产品推荐

