【问题标题】:Stringify causing SyntaxError: Unexpected end of JSON input React ExpressStringify 导致 SyntaxError:JSON 输入意外结束 React Express
【发布时间】:2022-01-20 10:05:50
【问题描述】:

我正在构建一个记录用户输入并将其保存到数据库的 React 应用程序。我使用 node.js Express 创建了一个 REST API 来将应用程序连接到数据库。

我已经成功地使用 Postman 发出了一个发布请求,但由于我收到 SyntaxError: Unexpected end of JSON input error,所以无法让它与 react 应用程序一起使用。

我已经尝试过类似 Stack Overflow 帖子中描述的解决方案,但对我没有任何帮助。据我所知,我已经正确格式化了我的 json 输入。一篇文章指出这可能与我的 Express API 没有返回格式正确的 JSON 有关,但事实并非如此,因为邮递员在成功发布后会收到 JSON 输出。

邮递员输出

控制台输出

堆栈跟踪

React 应用代码

import logo from './logo.svg';
import './App.css';
import Submit from './Submit';

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      pic: "",
      alive: "",
      typeOfDeath: "",
      comment: "",
      geoLocation: ""
    };
  }
 
  // code credit - https://github.com/lyhd/reactjs/blob/base64_encode/src/index.js
  encodeFileBase64 = (file) => {
    var reader = new FileReader();
    if (file) {
      reader.readAsDataURL(file);
      reader.onload = () => {
        var Base64 = reader.result;
        console.log(Base64);
        //setFileBase64String(Base64);
      };
      reader.onerror = (error) => {
        console.log("error: ", error);
      };
    }
  };

  handleSubmit = async (evt) => {
    evt.preventDefault();
    const input = document.querySelector('input[type=file]');
    const pic = input.files[0];
    const pic_base64 = this.encodeFileBase64(pic);
    const rbs = document.querySelectorAll('input[name="mortality"]');
    let alive = false;
    //code belongs to javascripttutorial.net/javascript-dom/javascript-radio-button/
    for (const rb of rbs) {
      if (rb.checked) {
        alive = rb.value;
        break;
      }
    }
    const typeOfDeathDropDown = document.querySelector('#typeOfDeath');
    const typeOfDeath = typeOfDeathDropDown.options[typeOfDeathDropDown.selectedIndex].value;
    const comment = document.querySelector('#comment').value.trim();
    const geoLocation = "placeholder";
    //pleaceholder validation if statement, replace at a later date
    if (1 > 0) {
      console.log(alive,typeOfDeath,comment,geoLocation);
      this.setState({
        pic: pic_base64,
        alive: alive,
        typeOfDeath: typeOfDeath,
        comment: comment,
        geoLocation: geoLocation
      });
      const url = 'https://zapp.ogs17.brighton.domains/';
      let jsonBody = JSON.stringify({
        pic: pic_base64,
        alive: alive,
        typeOfDeath: typeOfDeath,
        comment: comment,
        geoLocation: geoLocation
      });
      console.log(jsonBody);
      try {
        const response = await fetch(url, {
          method: 'POST', 
          headers: {'Content-Type':'application/x-www-form-urlencoded'},
          body: jsonBody
        });
        await console.log(response);
        const jsonData = await response.json();
        this.setState({
          loading: false,
          records: jsonData.records
        });
      } catch (err) {
        console.log(err);
        this.setState({
          loading: false,
          records: []
        });
      }
    }
  }

  render = () => {
    return (
      <div>
        <h1>Zapp App - Pangolin Sightings</h1>
        <form onSubmit={this.handleSubmit}>
          <input type="file" accept="image/*" id="pic" />

          <input id="alive" type="radio" name="mortality" />
          <label htmlFor="alive">Alive</label>
          <input id="deceased" type="radio" name="mortality" />
          <label htmlFor="deceased">Deceased</label>
          <br />

          <label htmlFor="typeOfDeath">Type of Death:</label>
          <select name="typeOfDeath" id="typeOfDeath">
            <option value="fence_electrocution">Fence death: electrocution;</option>
            <option value="fence_normal">Fence death: caught on non-electrified fence</option>
            <option value="road">Road death</option>
            <option value="other">Other</option>
          </select>
          <br />

          <textarea name="comment" id="comment" defaultValue="comment"></textarea>
          <br />

          
          <button type="submit">Submit</button>
        </form>
        <Submit state={this.state} />
      </div>
    );
  }
}
export default App;

Node.js Express Api 代码

const express = require('express');
const bodyParser = require('body-parser');
const db = require('./db');
const cors = require('cors');

const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
const port = 3000;

//https://stackoverflow.com/questions/18310394/no-access-control-allow-origin-node-apache-port-issue
app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', '*');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);

    // Pass to next layer of middleware
    next();
});

async function getSighting(req) {
    let status = 500, data = null;
    try {
        const oid = req.query.oid;
        if (oid
            && oid.length > 0 && oid.length <= 32
            && oid.match(/^[a-z0-9]+$/i)) {
            const sql = 'SELECT * FROM tSightings WHERE oid=?';
            const rows = await db.query(sql, [oid]);

            if (rows) {
                status = 200;
                data = {
                    'oid': oid,
                    'data': rows
                };
            } else {
                status = 204;
            }
        } else {
            status = 400;
        }
    } catch (e) {
        console.error(e);
    }
    return { status, data };
}

async function postSighting(req) {
    console.log("postSighting method entered")
    let status = 500, data = null;
    try {
        const pic = req.body.pic;
        const alive = req.body.alive;
        const typeOfDeath = req.body.typeOfDeath;
        const comment = req.body.comment;
        const geoLocation = req.body.geoLocation;
        //impliment appropriate checks here
        if (1 == 1) {
            const sql = 'INSERT INTO tSightings (pic, alive, typeOfDeath, comment, geoLocation) '
                + 'VALUES (?, ?, ?, ?, ?)';
            const result = await db.query(sql, [pic, alive, typeOfDeath, comment, geoLocation]);

            if (result.affectedRows) {
                status = 201;
                data = { 'id': result.insertId };
            }
        } else {
            status = 400;
        }
    } catch (e) {
        console.error(e);
    }
    return { status, data };
}

app.get('/', async (req, res) => {
    console.log("express get submitted")
    const { status, data } = await getSighting(req);
    res.status(status);
    if (data) res.json(data);
    else res.end();
})
app.listen(port, () => {
    console.log(`Running at http://localhost:${port}`)
})

app.get('/express_api', async (req, res) => {
    console.log("express get submitted")
    const { status, data } = await getData(req);
    res.status(status);
    if (data) res.json(data);
    else res.end();
})

app.post('/', async (req, res) => {
    const { status, data } = await postSighting(req);
    res.status(status);
    if (data) res.json(data);
    else res.end();
})

app.put('/express_api', async (req, res) => {
    res.status(405);
    res.end();
})

app.delete('/express_api', async (req, res) => {
    res.status(405);
    res.end();
})

【问题讨论】:

    标签: javascript node.js reactjs express fetch


    【解决方案1】:

    前端

    当遇到网络错误或服务器端的 CORS 配置错误时,fetch() 承诺将拒绝并返回 TypeError。

    您正在发送标头内容类型 = form-urlencoded 和实际正文为 json

      try {
            const response = await fetch(url, {
              method: 'POST', 
               //should be application json if your're stringifying it
              headers: {'Content-Type':' 'application/json'},
              body: jsonBody
            });
            await console.log(response);
           // need to check if the response is valid or not
            if(response.ok){
            const jsonData = await response.json();
           }else{
              throw response
            }
           
            this.setState({
              loading: false,
              records: jsonData.records
            });
          } 
    

    后端

    我不知道您为什么要设置自定义标头,同时使用允许所有方法的 cors app.use(cors());

    删除那个

    //was missing
    app.use(bodyParser.json());
    const corsOptions =  {
     origin: "*" // grant for all origins. can be use as per your ref url                     
    }
    app.use(cors(corsOptions));
    // don't need this part
    app.use(function (req, res, next) {
    
        // Website you wish to allow to connect
        res.setHeader('Access-Control-Allow-Origin', '*');
    
        // Request methods you wish to allow
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    
        // Request headers you wish to allow
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    
        // Set to true if you need the website to include cookies in the requests sent
        // to the API (e.g. in case you use sessions)
        res.setHeader('Access-Control-Allow-Credentials', true);
    
        // Pass to next layer of middleware
        next();
    });
    
    
    

    【讨论】:

    • 感谢您的回复。我听从了您的建议,不再收到语法错误消息,但我仍然收到 500。POST zapp.ogs17.brighton.domains net::ERR_ABORTED 500
    • 是的,已删除
    • 你能告诉我zapp.ogs17.brighton.domains url 在 express 应用中代表哪个 api 吗?
    • 适用于以下代码 app.post('/', async (req, res) => { const { status, data } = await postSighting(req); res.status(status) ; if (data) res.json(data); else res.end(); })
    • 因为 postman 和 curl 不绑定任何预检标头。和浏览器一样。如果可行,我做了一些更改,将其标记为已回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 2021-05-01
    • 2018-08-24
    • 1970-01-01
    • 2017-04-20
    • 2020-12-05
    相关资源
    最近更新 更多