React+Node+SQL Server应用GET请求出现500内部服务器错误排查
问题描述
在React+Node+SQL Server应用中,输入订单号触发GET请求http://localhost:5000/api/customerOrder/141798时出现500(Internal Server Error)。该订单号在数据库中存在,且通过POSTMAN调用该GET接口能正常返回数据。以下是相关代码,请求排查问题并给出修复方案:
dboperations.js
var config = require('./dbconfig'); const sql = require('mssql'); const { response } = require('express'); async function getallcustomerOrders(){ try{ let pool = await sql.connect(config); let orders = await pool.request() .query("SELECT * FROM [100].[dbo].[OELINCMT_SQL] order by ID desc"); return orders.recordsets; } catch (error){ console.log(error); } } async function getcustomerOrders(orderNumber) { try { let pool = await sql.connect(config); let orders = await pool.request() .input('input_parameter', sql.NChar, orderNumber) .query("SELECT ID,cmt FROM [100].[dbo].[OELINCMT_SQL] where LTRIM(ord_no) = LTRIM(@input_parameter)"); return orders.recordsets; } catch (error) { console.log(error); } } async function updateComments(ID) { try { let pool = await sql.connect(config); let orders = await pool.request() .input('ID', sql.NChar, ID) .query(`SELECT cmt FROM [100].[dbo].[OELINCMT_SQL] WHERE ID = @ID`); let order = orders.recordset.length ? orders.recordset[0] : null; if (order) { await pool.request() .input('cmt', req.body.cmt) .query(`UPDATE [100].[dbo].[OELINCMT_SQL] SET cmt = @cmt WHERE ID = @ID;`); order = { ...order, ...req.body }; response.json(order); } else { response.status(404).json({ message: 'Record not found' }); } } catch (error) { response.status(500).json(error); } } module.exports = { getallcustomerOrders : getallcustomerOrders, getcustomerOrders : getcustomerOrders, updateComments : updateComments }
api.js
var Db = require('./dboperations'); var dboperations = require('./dboperations'); var express = require('express'); var bodyParser = require('body-parser'); var cors = require('cors'); const { request, response } = require('express'); var app = express(); var router = express.Router(); app.use(bodyParser.urlencoded({ extended: true})); app.use(bodyParser.json()); app.use(cors()); app.use('/api', router); router.use((request,response,next)=> { console.log('middleware'); next(); }) router.route('/customerOrder').get((request,response)=>{ dboperations.getallcustomerOrders().then(result => { response.json(result[0]); console.log(result[0]); }) }) router.route('/customerOrder/:orderNumber').get((request,response)=>{ dboperations.getcustomerOrders(request.params.orderNumber).then(result => { response.json(result[0]); console.log(result[0]); }) }) router.route('/customerOrder/:ID').put((request,response)=>{ dboperations.updateComments(request.params.ID).then(result => { response.json(result[0]); console.log(result[0]); }) }) var port = process.env.PORT || 5000; app.listen(port); console.log('Customer Order API is running at ' + port); dboperations.getcustomerOrders().then(result => { console.log(result); }) dboperations.getallcustomerOrders().then(result => { console.log(result); }) dboperations.updateComments().then(result => { console.log(result); })
EmailFaxDetails.js
import React, { useState,useEffect } from 'react' import FetchOrderDetails from './FetchOrderDetails'; import axios from 'axios' import '../App.css'; const EmailFaxDetails = () => { const [orderNumber, setOrderNumber] = useState(''); const [id, setId] = useState([]); const [isShown, setIsShown] = useState(false); const url = `http://localhost:5000/api/customerOrder/${orderNumber}` useEffect(() => { axios.get(url) .then(response => { setId(response.data) console.log(response.data) }) .catch((err) => console.log(err)); }, [url]); const handleChange = event => { setOrderNumber(event.target.value); console.log(event.target.value); }; const handleClick = event => { event.preventDefault(); setIsShown(true); console.log(orderNumber); } return( <div> <br></br> <br></br> Order Number: <input placeholder="Order Number" type="text" id="message" name="message" onChange={handleChange} value={orderNumber} autoComplete="off" /> {id.map((idnum) => ( <div key={idnum.ID}> {isShown && <FetchOrderDetails ord_no={orderNumber} cmt={idnum.cmt} id={idnum.ID}/>} </div> ))} <button onClick={handleClick}>Search</button> </div> ) } export default EmailFaxDetails;
问题排查与修复方案
核心问题分析
前端请求时机错误:
组件挂载时orderNumber为空字符串,useEffect依赖url会自动发起请求http://localhost:5000/api/customerOrder/,虽然HTTP方法是GET,但服务端启动时的无参数函数调用已导致内部错误。服务端
updateComments函数致命缺陷:- 未接收
req、res参数却直接使用,导致变量未定义抛出错误; - 启动时被无参数调用,直接触发内部报错。
- 未接收
服务端接口无错误捕获:
getcustomerOrders等函数捕获错误后仅打印,未向上抛出,导致路由层无法处理异常,最终返回500错误。
修复步骤
1. 修复前端请求逻辑
改为点击搜索按钮后才发起请求,避免空参数请求:
import React, { useState } from 'react' import FetchOrderDetails from './FetchOrderDetails'; import axios from 'axios' import '../App.css'; const EmailFaxDetails = () => { const [orderNumber, setOrderNumber] = useState(''); const [id, setId] = useState([]); const [isShown, setIsShown] = useState(false); const fetchOrderData = async () => { if (!orderNumber.trim()) return; try { const url = `http://localhost:5000/api/customerOrder/${orderNumber}`; const response = await axios.get(url); setId(response.data); setIsShown(true); } catch (err) { console.log(err); setIsShown(false); } }; const handleChange = event => { setOrderNumber(event.target.value); }; const handleClick = event => { event.preventDefault(); fetchOrderData(); }; return( <div> <br></br> <br></br> Order Number: <input placeholder="Order Number" type="text" id="message" name="message" onChange={handleChange} value={orderNumber} autoComplete="off" /> {isShown && id.map((idnum) => ( <div key={idnum.ID}> <FetchOrderDetails ord_no={orderNumber} cmt={idnum.cmt} id={idnum.ID}/> </div> ))} <button onClick={handleClick}>Search</button> </div> ) } export default EmailFaxDetails;
2. 修复服务端updateComments函数
补充参数并修正内部逻辑:
async function updateComments(ID, req, res) { try { let pool = await sql.connect(config); let orders = await pool.request() .input('ID', sql.NChar, ID) .query(`SELECT cmt FROM [100].[dbo].[OELINCMT_SQL] WHERE ID = @ID`); let order = orders.recordset.length ? orders.recordset[0] : null; if (order) { await pool.request() .input('ID', sql.NChar, ID) .input('cmt', sql.NChar, req.body.cmt) .query(`UPDATE [100].[dbo].[OELINCMT_SQL] SET cmt = @cmt WHERE ID = @ID;`); order = { ...order, ...req.body }; res.json(order); } else { res.status(404).json({ message: 'Record not found' }); } } catch (error) { console.log(error); res.status(500).json({ error: error.message }); } }
3. 修复服务端路由与启动错误
删除无参数调用,添加错误捕获,修正PUT路由调用:
var dboperations = require('./dboperations'); var express = require('express'); var bodyParser = require('body-parser'); var cors = require('cors'); var app = express(); var router = express.Router(); app.use(bodyParser.urlencoded({ extended: true})); app.use(bodyParser.json()); app.use(cors()); app.use('/api', router); router.use((request,response,next)=> { console.log('middleware'); next(); }) router.route('/customerOrder').get((request,response)=>{ dboperations.getallcustomerOrders() .then(result => response.json(result[0])) .catch(err => response.status(500).json({ error: err.message })) }) router.route('/customerOrder/:orderNumber').get((request,response)=>{ dboperations.getcustomerOrders(request.params.orderNumber) .then(result => response.json(result[0])) .catch(err => response.status(500).json({ error: err.message })) }) router.route('/customerOrder/:ID').put((request,response)=>{ dboperations.updateComments(request.params.ID, request, response) .catch(err => response.status(500).json({ error: err.message })) }) var port = process.env.PORT || 5000; app.listen(port); console.log('Customer Order API is running at ' + port);
4. 补充服务端函数错误抛出
修改getallcustomerOrders和getcustomerOrders,让错误能被路由层捕获:
async function getallcustomerOrders(){ try{ let pool = await sql.connect(config); let orders = await pool.request() .query("SELECT * FROM [100].[dbo].[OELINCMT_SQL] order by ID desc"); return orders.recordsets; } catch (error){ console.log(error); throw error; } } async function getcustomerOrders(orderNumber) { try { let pool = await sql.connect(config); let orders = await pool.request() .input('input_parameter', sql.NChar, orderNumber) .query("SELECT ID,cmt FROM [100].[dbo].[OELINCMT_SQL] where LTRIM(ord_no) = LTRIM(@input_parameter)"); return orders.recordsets; } catch (error) { console.log(error); throw error; } }
内容的提问来源于stack exchange,提问作者Glenn94
相关产品推荐
相关产品推荐

