You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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应用的应用设置中已配置相关变量:

  1. 登录Azure门户,进入目标静态Web应用
  2. 进入「配置」→「应用设置」
  3. 添加CPL_API_USER_PROD、CPL_API_PSWRD_PROD、NEXT_PUBLIC_CPL_API_URL_PROD,值与GitHub Secrets一致
  4. 保存后重启应用

4. 排查API网络连通性

Azure静态Web应用的出站IP可能被目标API拦截:

  • 在Azure门户的静态Web应用「概述」中获取出站IP列表,确认目标API已允许这些IP访问
  • 可在代码中临时添加日志,输出API请求的完整URL和响应内容,排查请求是否正常发送

内容的提问来源于stack exchange,提问作者aadlc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:37:19