Stringify 导致语法错误:JSON 输入意外结束 React Express

问题描述 投票:0回答:1

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

我已使用 Postman 成功发出发布请求,但无法使其与 React 应用程序一起使用,因为我收到此错误:

语法错误:JSON 输入意外结束

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

邮递员输出:

Postman output

控制台输出:

console output

堆栈跟踪:

console stack trace

反应应用程序代码:

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个回答
1
投票

前端

当遇到网络错误或服务器端 CORS 配置错误时,fetch() Promise 将拒绝并返回 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();
});


© www.soinside.com 2019 - 2024. All rights reserved.