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

Next.js电商网站/api/orders接口报错求助

电商网站接口报错排查(Next.js + Material-UI + Sanity)

我用Next.js开发电商网站,搭配Material-UI和Sanity,现在遇到接口报错,信息如下:

error: API resolved without sending a response for /api/orders, this may result in stalled requests
API handler should not return a value, received function.

怀疑问题出在placeOrderHandler函数,相关代码如下:

前端组件代码

function PlaceOrderScreen() {
  const { enqueueSnackbar } = useSnackbar();
  const [loading, setLoading] = useState(false);
  const router = useRouter();
  const { state, dispatch } = useContext(Store);
  const {
    userInfo,
    cart: { cartItems, shippingAddress, paymentMethod },
  } = state;
  const round2 = (num) => Math.round(num * 100 + Number.EPSILON) / 100; // 123.456 => 123.46
  const itemsPrice = round2(
    cartItems.reduce((a, c) => a + c.price * c.quantity, 0)
  );
  const shippingPrice = itemsPrice > 100 ? 0 : 20;
  const taxPrice = round2(itemsPrice * 0.15);
  const totalPrice = round2(itemsPrice + shippingPrice + taxPrice);

  useEffect(() => {
    if (!paymentMethod) {
      router.push('/payment');
    }
    if (cartItems.length === 0) {
      router.push('/cart');
    }
  }, [cartItems, paymentMethod, router]);

  const placeOrderHandler = async () => {
    try {
      setLoading(true);
      const { data } = await axios.post(
        '/api/orders',
        {
          orderItems: cartItems.map((x) => ({
            ...x,
            countInStock: undefined,
            slug: undefined,
          })),
          shippingAddress,
          paymentMethod,
          itemsPrice,
          shippingPrice,
          taxPrice,
          totalPrice,
        },
        {
          headers: {
            authorization: `Bearer ${userInfo.token}`,
          },
        }
        );
        
      
      dispatch({ type: 'CART_CLEAR' });
      jsCookie.remove('cartItems');
      setLoading(false);
      router.push(`/order/${data}`);
    } catch (err) {
      setLoading(false);
      enqueueSnackbar(getError(err), { variant: 'error' });
    }
    
  };
  return (
    <Layout title="Place Order">
      <CheckoutWizard activeStep={3}></CheckoutWizard>
      <Typography component="h1" variant="h1">
        Place Order
      </Typography>
      

      <Grid item md={3} xs={12}>
  <Card sx={classes.section}>
    <List>
      <ListItem>
        <Typography variant="h2">Order Summary</Typography>
      </ListItem>
      <ListItem>
        <Grid container>
          <Grid item xs={6}>
            <Typography>Items:</Typography>
          </Grid>
          <Grid item xs={6}>
            <Typography align="right">${itemsPrice}</Typography>
          </Grid>
        </Grid>
      </ListItem>
      <ListItem>
        <Grid container>
          <Grid item xs={6}>
            <Typography>Shipping:</Typography>
          </Grid>
          <Grid item xs={6}>
            <Typography align="right">${shippingPrice}</Typography>
          </Grid>
        </Grid>
      </ListItem>
      <ListItem>
        <Grid container>
          <Grid item xs={6}>
            <Typography>
              <strong>Total:</strong>
            </Typography>
          </Grid>
          <Grid item xs={6}>
            <Typography align="right">
              <strong>${totalPrice}</strong>
            </Typography>
          </Grid>
        </Grid>
      </ListItem>
      <ListItem>
        <Button
          onClick={placeOrderHandler}
          variant="contained"
          color="primary"
          fullWidth
          disabled={loading}
        >
          Place Order
        </Button>
      </ListItem>
      {loading && (
        <ListItem>
          <CircularProgress />
        </ListItem>
      )}
    </List>
  </Card>
</Grid>

    </Layout>
  );
}
export default dynamic(() => Promise.resolve(PlaceOrderScreen), { ssr: false });

API处理器代码

import axios from 'axios';

import { isAuth } from '../../../utils/auth';
import config from '../../../utils/config';
import { createRouter } from 'next-connect';



const router = createRouter();

router.use(isAuth);

  
router.post(async (req, res) => { 
  const projectId = config.projectId;
  const dataset = config.dataset;
  const tokenWithWriteAccess = process.env.SANITY_AUTH_TOKEN;
  const { data } = await axios.post(
    `https://${projectId}.api.sanity.io/v1/data/mutate/${dataset}?returnIds=true`,
    {
      mutations: [
        {
          create: {
            _type: 'order',
            createdAt: new Date().toISOString(),
            ...req.body,
            userName: req.user.name,
            user: {
              _type: 'reference',
              _ref: req.user._id,
            },
          },
        },
      ],
    },
    {
      headers: {
        'Content-type': 'application/json',
        Authorization: `Bearer ${tokenWithWriteAccess}`,
      },
    }
  );

  res.status(201).send(data.results[0].id);
});
export default router.handler;

问题分析与修复方案

这两个报错的核心问题集中在API路由的响应处理逻辑上:

1. API handler should not return a value, received function 报错原因

Next.js API路由要求处理器要么发送响应,要么调用next()传递流程,不能返回函数。问题大概率出在isAuth中间件上:

  • 如果isAuth验证失败时没有直接发送响应(比如res.status(401).json(...)),而是返回了函数或者没有终止流程,就会触发这个错误。

修复isAuth中间件:
确保验证失败时立即发送响应并终止流程,验证通过才调用next()传递到后续处理:

export const isAuth = async (req, res, next) => {
  const authorization = req.headers.authorization;
  if (!authorization || !authorization.startsWith('Bearer ')) {
    return res.status(401).json({ message: '未提供有效令牌' });
  }
  // 解析令牌并验证用户逻辑...
  const token = authorization.split(' ')[1];
  try {
    // 你的验证逻辑,比如解码JWT获取用户信息
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next(); // 验证通过才继续执行后续路由
  } catch (err) {
    return res.status(401).json({ message: '令牌无效' });
  }
};

2. API resolved without sending a response 报错原因

这个错误说明请求进入API路由后,没有任何响应被发送,可能的场景:

  • isAuth中间件未处理异常,导致请求卡在中间件流程中
  • post处理函数中的异步操作出错,但没有捕获错误并返回响应

修复API路由的post处理函数:
添加错误捕获逻辑,确保任何异常都能返回响应:

router.post(async (req, res) => { 
  try {
    const projectId = config.projectId;
    const dataset = config.dataset;
    const tokenWithWriteAccess = process.env.SANITY_AUTH_TOKEN;
    const { data } = await axios.post(
      `https://${projectId}.api.sanity.io/v1/data/mutate/${dataset}?returnIds=true`,
      {
        mutations: [
          {
            create: {
              _type: 'order',
              createdAt: new Date().toISOString(),
              ...req.body,
              userName: req.user.name,
              user: {
                _type: 'reference',
                _ref: req.user._id,
              },
            },
          },
        ],
      },
      {
        headers: {
          'Content-type': 'application/json',
          Authorization: `Bearer ${tokenWithWriteAccess}`,
        },
      }
    );

    res.status(201).send(data.results[0].id);
  } catch (err) {
    // 捕获所有异常并返回错误响应
    res.status(500).json({ message: err.message || '创建订单失败' });
  }
});

3. 验证路由导出方式

如果使用的是Next.js Pages Router,确保正确调用handler()方法导出:

export default router.handler(); // 注意要调用handler方法,而不是直接导出handler属性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:07:36