迁移Clarifai API密钥至后端后人脸检测失效,报TypeError错误
问题
为避免Clarifai API密钥在控制台暴露,将人脸检测的API密钥迁移至后端后,人脸检测功能完全失效,控制台报错:
TypeError: data.outputs is undefined
calculateFaceLocation App.js:49
onButtonSubmit App.js:95
promise callback*./src/App.js/</App/this.onButtonSubmit App.js:72
React 23
js index.js:8
js main.chunk.js:2298
Webpack 7
后端主文件代码
const express = require('express'); const bodyParser = require('body-parser'); const bcrypt = require('bcrypt-nodejs'); const cors = require('cors'); const knex = require('knex'); const register = require('./controllers/register'); const signin = require('./controllers/signin'); const profile = require('./controllers/profile'); const image = require('./controllers/image'); const db = knex({ client: 'pg', connection: { host : '127.0.0.1', user : 'postgres', port: 5432, password : 'Moshe6700', database : 'smart-brain' } }); const app = express(); app.use(bodyParser.json()) app.use(cors()) app.get('/', (req, res) => {res.send(database.users) }) app.post('/signin', (req, res) => {signin.handleSignin(req, res, db, bcrypt)}) app.post('/register', (req, res) => {register.handleRegister(req, res, db, bcrypt)}) app.get('/profile/:id', (req, res) => {profile.handleProfile(req, res, db)}) app.put('/image', (req, res) => {image.handleImage(req, res, db)}) app.post('/imageurl', (req, res) => {image.handleApiCall(req, res)}) app.listen(3001, () => { console.log('app is running on port 3001') })
后端image.js代码
const Clarifai = require('clarifai'); const app = new Clarifai.App({ apiKey: '489560069986431e89aa152fe709ba94' }); const handleApiCall = (req, res) => { app.models .predict(Clarifai.FACE_DETECT_MODEL, req.body.input) .then(data => { res.json(data); }) .catch(err => res.status(400).json('unable to work with API')) } const handleImage = (req, res, db) => { const { id } = req.body; db('users').where('id', '=', id) .increment('entries', 1) .returning('entries') .then(entries => { res.json(entries[0].entries) }) .catch(err => res.status(400).json('unable to get entries')) } module.exports = { handleImage:handleImage, handleApiCall:handleApiCall }
前端App.js代码
import React, {Component} from 'react'; import Navigation from './components/Navigation/Navigation'; import Logo from './components/Logo/Logo'; import ImageLinkForm from './components/ImageLinkForm/ImageLinkForm'; import FaceRecognition from './components/FaceRecognition/FaceRecognition'; import Rank from './components/Rank/Rank'; import Signin from './components/Signin/Signin'; import Register from './components/Register/Register'; import './App.css'; const intialState = { input: '', imageUrl: '', box: {}, route: 'signin', isSignedIn: false, user: { id: '', name: '', email: '', entries: 0, joined: '' } } class App extends Component { constructor() { super(); this.state = intialState } loadUser = (data) => { this.setState({user: { id: data.id, name: data.name, email: data.email, entries: data.entries, joined: data.joined }}) } calculateFaceLocation =(data) => { console.log(data) const clarifaiFace = data.outputs[0].data.regions[0].region_info.bounding_box; const image = document.getElementById('inputimage'); const width = Number(image.width); const height = Number(image.height); return { leftCol: clarifaiFace.left_col * width, topRow: clarifaiFace.top_row * height, rightCol: width - clarifaiFace.right_col * width, bottomRow: height - clarifaiFace.bottom_row * height, } } displayFaceBox = (box) => { console.log(box) this.setState({box: box}); } onInputChange = (event) => { this.setState({input: event.target.value}) } onButtonSubmit = () => { this.setState({imageUrl: this.state.input}) fetch('http://localhost:3001/imageurl', { method: 'post', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ input: this.state.input }) }) .then(response => response.json()) .then( response => { if (response) { fetch('http://localhost:3001/image', { method: 'put', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ id: this.state.user.id }) }) .then(response => response.json()) .then(count => { this.setState(Object.assign(this.state.user, { entries: count})) }) .catch(console.log) } this.displayFaceBox(this.calculateFaceLocation(response)) }) .catch(err => console.log(err)); } onRouteChange = (route) => { if (route === 'signout') { this.setState(intialState) } else if (route === 'home') { this.setState({isSignedIn: true}) } this.setState({route:route}); } render() { return ( <div className="App"> <Navigation isSignedIn={this.state.isSignedIn} onRouteChange={this.onRouteChange} /> { this.state.route === 'home' ? <div> <Logo /> <Rank name={this.state.user.name} entries={this.state.user.entries}/> <ImageLinkForm onInputChange={this.onInputChange} onButtonSubmit={this.onButtonSubmit} /> <FaceRecognition box={this.state.box} imageUrl={this.state.imageUrl}/> </div> :( this.state.route === 'signin' ?<Signin loadUser={this.loadUser} onRouteChange={this.onRouteChange} /> :<Register loadUser={this.loadUser} onRouteChange={this.onRouteChange} /> ) } </div> ); } } export default App;
问题原因分析
- 后端调用Clarifai API失败时,返回的是字符串
'unable to work with API',前端会将这个字符串传入calculateFaceLocation函数,但字符串没有outputs属性,直接触发报错。 - 前端的条件判断
if (response)逻辑失效:即使后端返回错误字符串,response依然为真,会继续执行人脸位置计算逻辑,而非进入错误处理分支。 - 前端更新用户entries的方式不符合React规范:直接通过
Object.assign(this.state.user, ...)修改state对象,但这不是当前报错的直接原因。
修复方案
1. 后端返回格式统一修改
将错误返回改为对象格式,方便前端识别区分成功/失败状态:
// image.js中handleApiCall的catch部分修改为 .catch(err => res.status(400).json({ success: false, error: 'unable to work with API' })) // 成功返回也统一格式 .then(data => { res.json({ success: true, data: data }); })
2. 前端逻辑调整
在处理/imageurl响应时,先判断请求是否成功,再执行后续逻辑:
// onButtonSubmit中的then回调修改为 .then(response => { if (response.success) { fetch('http://localhost:3001/image', { method: 'put', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ id: this.state.user.id }) }) .then(response => response.json()) .then(count => { // 正确更新state的方式 this.setState(prevState => ({ user: { ...prevState.user, entries: count } })) }) .catch(console.log) // 传入正确的data字段 this.displayFaceBox(this.calculateFaceLocation(response.data)) } else { console.log(response.error); // 可在此添加用户可见的错误提示 } })
内容的提问来源于stack exchange,提问作者Morris
相关产品推荐
相关产品推荐

