使用地点 api 照片 returns "Unexpected token � in JSON at position 0"

Using places api photo returns "Unexpected token � in JSON at position 0"

我目前正在玩 google 个地方 api 以获得一些舒适度,但我遇到了一个让我有点难过的问题,我是希望有人可以在这里纠正我。我正在 api 调用本地代理服务器,到目前为止一切正常,除非我尝试为特定业务提取图像。

我已经做到了控制台记录 get 请求的每个单独部分 url 以确保它被正确连接并且在这方面一切看起来都是正确的(我可以将它直接粘贴到浏览器并获得预期结果)。我有一种预感,这是一个一般的代码问题,而不是特定于地方 api 本身的问题,但是如果对此有任何见解,我们将不胜感激。

来自代理服务器的终端错误(看起来不是很有用,但其他人可能会认出我不是的东西):

undefined:1 ���� ^

SyntaxError: Unexpected token � in JSON at position 0

前端代码:

function makeAPICall(url) {
  return fetch(url);
}

makeAPICall(`http://localhost:5000/places/${searchString}`)
      .then(response => {
        return response.json();
      })
      .then(data => {
        responseObject = data.results;
        renderResult(responseObject[10]);

        return responseObject[10];
      })
      .then(responseObject => {
        makeAPICall(`http://localhost:5000/place/image/${responseObject.photos[0].photo_reference}`)
          .then(response => {
            return response.json();
          })
          .then(photoData => {
            console.log(photoData);
          });
      });

代理代码(减去与问题无关的工作路线):

require('dotenv').config({path: '../.env'});

const express = require('express');
const request = require('request');

const GOOGLE_API_KEY = process.env.API_KEY;
const placesImgURL = 'https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=';

const app = express();

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

app.get('/place/image/:query', (req, res) => {
  request(
    { url: `${placesImgURL}${req.params.query}&key=${GOOGLE_API_KEY}` },
    (error, response, body) => {
      if (error || response.statusCode !== 200) {
        console.log(response);
      }

      res.json(JSON.parse(body));
    }
  )
});

const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`listening on ${PORT}`));

更新:

服务器代码中触发错误的行是:

res.json(JSON.parse(body));

开发工具中的网络调用屏幕截图:

当控制台记录抛出错误的行时,我得到以下信息(公平警告,这是完整的日志,而且非常长):

ServerResponse {
  _events: { finish: [Function: bound resOnFinish] },
  _eventsCount: 1,
  _maxListeners: undefined,
  output: [],
  outputEncodings: [],
  outputCallbacks: [],
  outputSize: 0,
  writable: true,
  _last: false,
  chunkedEncoding: false,
  shouldKeepAlive: true,
  useChunkedEncodingByDefault: true,
  sendDate: true,
  _removedConnection: false,
  _removedContLen: false,
  _removedTE: false,
  _contentLength: 22631,
  _hasBody: true,
  _trailer: '',
  finished: true,
  _headerSent: true,
  socket:
   Socket {
     connecting: false,
     _hadError: false,
     _handle:
      TCP {
        reading: true,
        onread: [Function: onStreamRead],
        onconnection: null,
        _consumed: true,
        [Symbol(owner)]: [Circular] },
     _parent: null,
     _host: null,
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: BufferList { head: null, tail: null, length: 0 },
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        emitClose: false,
        destroyed: false,
        defaultEncoding: 'utf8',
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: true,
     _events:
      { end: [Array],
        drain: [Array],
        timeout: [Function: socketOnTimeout],
        data: [Function: bound socketOnData],
        error: [Function: socketOnError],
        close: [Array],
        resume: [Function: onSocketResume],
        pause: [Function: onSocketPause] },
     _eventsCount: 8,
     _maxListeners: undefined,
     _writableState:
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        finalCalled: false,
        needDrain: true,
        ending: false,
        ended: false,
        finished: false,
        destroyed: false,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function: bound onwrite],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 3,
        prefinished: false,
        errorEmitted: false,
        emitClose: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     allowHalfOpen: true,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server:
      Server {
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [TCP],
        _usingWorkers: false,
        _workers: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        keepAliveTimeout: 5000,
        _pendingResponseData: 0,
        maxHeadersCount: null,
        _connectionKey: '6::::5000',
        [Symbol(IncomingMessage)]: [Function],
        [Symbol(ServerResponse)]: [Function],
        [Symbol(asyncId)]: 5 },
     _server:
      Server {
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [TCP],
        _usingWorkers: false,
        _workers: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        keepAliveTimeout: 5000,
        _pendingResponseData: 0,
        maxHeadersCount: null,
        _connectionKey: '6::::5000',
        [Symbol(IncomingMessage)]: [Function],
        [Symbol(ServerResponse)]: [Function],
        [Symbol(asyncId)]: 5 },
     timeout: 120000,
     parser:
      HTTPParser {
        '0': [Function: parserOnHeaders],
        '1': [Function: parserOnHeadersComplete],
        '2': [Function: parserOnBody],
        '3': [Function: parserOnMessageComplete],
        '4': [Function: bound onParserExecute],
        _headers: [],
        _url: '',
        _consumed: true,
        socket: [Circular],
        incoming: [IncomingMessage],
        outgoing: null,
        maxHeaderPairs: 2000,
        onIncoming: [Function: bound parserOnIncoming] },
     on: [Function: socketOnWrap],
     _paused: false,
     _httpMessage: [Circular],
     [Symbol(asyncId)]: 8,
     [Symbol(lastWriteQueueSize)]: 0,
     [Symbol(timeout)]:
      Timeout {
        _called: false,
        _idleTimeout: 120000,
        _idlePrev: [TimersList],
        _idleNext: [TimersList],
        _idleStart: 6746,
        _onTimeout: [Function: bound ],
        _timerArgs: undefined,
        _repeat: null,
        _destroyed: false,
        [Symbol(unrefed)]: true,
        [Symbol(asyncId)]: 9,
        [Symbol(triggerId)]: 8 },
     [Symbol(kBytesRead)]: 0,
     [Symbol(kBytesWritten)]: 0 },
  connection:
   Socket {
     connecting: false,
     _hadError: false,
     _handle:
      TCP {
        reading: true,
        onread: [Function: onStreamRead],
        onconnection: null,
        _consumed: true,
        [Symbol(owner)]: [Circular] },
     _parent: null,
     _host: null,
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: BufferList { head: null, tail: null, length: 0 },
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        emitClose: false,
        destroyed: false,
        defaultEncoding: 'utf8',
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: true,
     _events:
      { end: [Array],
        drain: [Array],
        timeout: [Function: socketOnTimeout],
        data: [Function: bound socketOnData],
        error: [Function: socketOnError],
        close: [Array],
        resume: [Function: onSocketResume],
        pause: [Function: onSocketPause] },
     _eventsCount: 8,
     _maxListeners: undefined,
     _writableState:
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        finalCalled: false,
        needDrain: true,
        ending: false,
        ended: false,
        finished: false,
        destroyed: false,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function: bound onwrite],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 3,
        prefinished: false,
        errorEmitted: false,
        emitClose: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     allowHalfOpen: true,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server:
      Server {
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [TCP],
        _usingWorkers: false,
        _workers: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        keepAliveTimeout: 5000,
        _pendingResponseData: 0,
        maxHeadersCount: null,
        _connectionKey: '6::::5000',
        [Symbol(IncomingMessage)]: [Function],
        [Symbol(ServerResponse)]: [Function],
        [Symbol(asyncId)]: 5 },
     _server:
      Server {
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _connections: 1,
        _handle: [TCP],
        _usingWorkers: false,
        _workers: [],
        _unref: false,
        allowHalfOpen: true,
        pauseOnConnect: false,
        httpAllowHalfOpen: false,
        timeout: 120000,
        keepAliveTimeout: 5000,
        _pendingResponseData: 0,
        maxHeadersCount: null,
        _connectionKey: '6::::5000',
        [Symbol(IncomingMessage)]: [Function],
        [Symbol(ServerResponse)]: [Function],
        [Symbol(asyncId)]: 5 },
     timeout: 120000,
     parser:
      HTTPParser {
        '0': [Function: parserOnHeaders],
        '1': [Function: parserOnHeadersComplete],
        '2': [Function: parserOnBody],
        '3': [Function: parserOnMessageComplete],
        '4': [Function: bound onParserExecute],
        _headers: [],
        _url: '',
        _consumed: true,
        socket: [Circular],
        incoming: [IncomingMessage],
        outgoing: null,
        maxHeaderPairs: 2000,
        onIncoming: [Function: bound parserOnIncoming] },
     on: [Function: socketOnWrap],
     _paused: false,
     _httpMessage: [Circular],
     [Symbol(asyncId)]: 8,
     [Symbol(lastWriteQueueSize)]: 0,
     [Symbol(timeout)]:
      Timeout {
        _called: false,
        _idleTimeout: 120000,
        _idlePrev: [TimersList],
        _idleNext: [TimersList],
        _idleStart: 6746,
        _onTimeout: [Function: bound ],
        _timerArgs: undefined,
        _repeat: null,
        _destroyed: false,
        [Symbol(unrefed)]: true,
        [Symbol(asyncId)]: 9,
        [Symbol(triggerId)]: 8 },
     [Symbol(kBytesRead)]: 0,
     [Symbol(kBytesWritten)]: 0 },
  _header:
   'HTTP/1.1 200 OK\r\nX-Powered-By: Express\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 22631\r\nETag: W/"5867-c3Fodr1eSCA3vlKX+kDMgudDONQ"\r\nDate: Wed, 26 Aug 2020 18:14:27 GMT\r\nConnection: keep-alive\r\n\r\n',
  _onPendingData: [Function: bound updateOutgoingData],
  _sent100: false,
  _expect_continue: false,
  req:
   IncomingMessage {
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: BufferList { head: null, tail: null, length: 0 },
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: null,
        ended: true,
        endEmitted: false,
        reading: false,
        sync: true,
        needReadable: false,
        emittedReadable: true,
        readableListening: false,
        resumeScheduled: false,
        emitClose: true,
        destroyed: false,
        defaultEncoding: 'utf8',
        awaitDrain: 0,
        readingMore: true,
        decoder: null,
        encoding: null },
     readable: true,
     _events: {},
     _eventsCount: 0,
     _maxListeners: undefined,
     socket:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [TCP],
        _parent: null,
        _host: null,
        _readableState: [ReadableState],
        readable: true,
        _events: [Object],
        _eventsCount: 8,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: true,
        allowHalfOpen: true,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Server],
        _server: [Server],
        timeout: 120000,
        parser: [HTTPParser],
        on: [Function: socketOnWrap],
        _paused: false,
        _httpMessage: [Circular],
        [Symbol(asyncId)]: 8,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]:
         Timeout {
           _called: false,
           _idleTimeout: 120000,
           _idlePrev: [TimersList],
           _idleNext: [TimersList],
           _idleStart: 6746,
           _onTimeout: [Function: bound ],
           _timerArgs: undefined,
           _repeat: null,
           _destroyed: false,
           [Symbol(unrefed)]: true,
           [Symbol(asyncId)]: 9,
           [Symbol(triggerId)]: 8 },
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0 },
     connection:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [TCP],
        _parent: null,
        _host: null,
        _readableState: [ReadableState],
        readable: true,
        _events: [Object],
        _eventsCount: 8,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: true,
        allowHalfOpen: true,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Server],
        _server: [Server],
        timeout: 120000,
        parser: [HTTPParser],
        on: [Function: socketOnWrap],
        _paused: false,
        _httpMessage: [Circular],
        [Symbol(asyncId)]: 8,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]:
         Timeout {
           _called: false,
           _idleTimeout: 120000,
           _idlePrev: [TimersList],
           _idleNext: [TimersList],
           _idleStart: 6746,
           _onTimeout: [Function: bound ],
           _timerArgs: undefined,
           _repeat: null,
           _destroyed: false,
           [Symbol(unrefed)]: true,
           [Symbol(asyncId)]: 9,
           [Symbol(triggerId)]: 8 },
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0 },
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers:
      { host: 'localhost:5000',
        connection: 'keep-alive',
        'user-agent':
         'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
        accept: '*/*',
        origin: 'http://localhost:3000',
        'sec-fetch-site': 'same-site',
        'sec-fetch-mode': 'cors',
        'sec-fetch-dest': 'empty',
        referer: 'http://localhost:3000/',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en-US,en;q=0.9',
        'if-none-match': 'W/"5868-+iRV2Q1kdgam9TbgiDgKID9Vs1s"' },
     rawHeaders:
      [ 'Host',
        'localhost:5000',
        'Connection',
        'keep-alive',
        'User-Agent',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
        'Accept',
        '*/*',
        'Origin',
        'http://localhost:3000',
        'Sec-Fetch-Site',
        'same-site',
        'Sec-Fetch-Mode',
        'cors',
        'Sec-Fetch-Dest',
        'empty',
        'Referer',
        'http://localhost:3000/',
        'Accept-Encoding',
        'gzip, deflate, br',
        'Accept-Language',
        'en-US,en;q=0.9',
        'If-None-Match',
        'W/"5868-+iRV2Q1kdgam9TbgiDgKID9Vs1s"' ],
     trailers: {},
     rawTrailers: [],
     aborted: false,
     upgrade: false,
     url: '/places/restaurants+near%20me',
     method: 'GET',
     statusCode: null,
     statusMessage: null,
     client:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [TCP],
        _parent: null,
        _host: null,
        _readableState: [ReadableState],
        readable: true,
        _events: [Object],
        _eventsCount: 8,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: true,
        allowHalfOpen: true,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: [Server],
        _server: [Server],
        timeout: 120000,
        parser: [HTTPParser],
        on: [Function: socketOnWrap],
        _paused: false,
        _httpMessage: [Circular],
        [Symbol(asyncId)]: 8,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]:
         Timeout {
           _called: false,
           _idleTimeout: 120000,
           _idlePrev: [TimersList],
           _idleNext: [TimersList],
           _idleStart: 6746,
           _onTimeout: [Function: bound ],
           _timerArgs: undefined,
           _repeat: null,
           _destroyed: false,
           [Symbol(unrefed)]: true,
           [Symbol(asyncId)]: 9,
           [Symbol(triggerId)]: 8 },
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0 },
     _consuming: false,
     _dumped: false,
     next: [Function: next],
     baseUrl: '',
     originalUrl: '/places/restaurants+near%20me',
     _parsedUrl:
      Url {
        protocol: null,
        slashes: null,
        auth: null,
        host: null,
        port: null,
        hostname: null,
        hash: null,
        search: null,
        query: null,
        pathname: '/places/restaurants+near%20me',
        path: '/places/restaurants+near%20me',
        href: '/places/restaurants+near%20me',
        _raw: '/places/restaurants+near%20me' },
     params: { query: 'restaurants+near me' },
     query: {},
     res: [Circular],
     route:
      Route { path: '/places/:query', stack: [Array], methods: [Object] } },
  locals: {},
  statusMessage: 'OK',
  statusCode: 200,
  [Symbol(isCorked)]: false,
  [Symbol(outHeadersKey)]:
   { 'x-powered-by': [ 'X-Powered-By', 'Express' ],
     'access-control-allow-origin': [ 'Access-Control-Allow-Origin', '*' ],
     'content-type': [ 'Content-Type', 'application/json; charset=utf-8' ],
     'content-length': [ 'Content-Length', '22631' ],
     etag: [ 'ETag', 'W/"5867-c3Fodr1eSCA3vlKX+kDMgudDONQ"' ] } }
_http_outgoing.js:470
    throw new ERR_HTTP_HEADERS_SENT('set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at ServerResponse.header (/Users/tyleranyan/Documents/api-practice/places/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/Users/tyleranyan/Documents/api-practice/places/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Users/tyleranyan/Documents/api-practice/places/node_modules/express/lib/response.js:267:15)
    at Request.request [as _callback] (/Users/tyleranyan/Documents/api-practice/places/proxy/server.js:25:11)
    at Request.self.callback (/Users/tyleranyan/Documents/api-practice/places/node_modules/request/request.js:185:22)
    at Request.emit (events.js:182:13)
    at Request.<anonymous> (/Users/tyleranyan/Documents/api-practice/places/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:182:13)
    at IncomingMessage.<anonymous> (/Users/tyleranyan/Documents/api-practice/places/node_modules/request/request.js:1076:12)

这里的问题是 request 库似乎已被弃用。如果你换成其他的如axios,下面的代码根据你自己的工作没有问题:

const express = require('express')
const axios = require('axios')

const url = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=CnRtAAAATLZNl354RwP_9UKbQ_5Psy40texXePv4oAlgP4qNEkdIrkyse7rPXYGd9D_Uj1rVsQdWT4oRz4QrYAJNpFX7rzqqMlZw2h2E2y5IKMUZ7ouD_SlcHxYq1yL4KbKUv3qtWgTK0A6QbGh87GB3sscrHRIQiG2RrmU_jF4tENr9wGS_YxoUSSDrYjWmrNfeEHSGSc3FyhNLlBU&key=YOUR_API_KEY"

const getData = async (url) => {
  try {
    const response = await axios.get(url)
    const data = response.data
    console.log('data', response.data)
  } catch (error) {
    console.log('error', error)
  }
}

const app = express()
const port = 3000

app.get('/', async (req, res) => {
  res.send(await getData(url))
})

app.listen(port, () => {})

输出:

Response GET / status=200
"data"
"����\u0000\u0010JFIF\u0000\u0001\u00…01b`J+]�Ǥ�%�„c����8�\u001fb\u0017/��..."

虽然响应也是 image; don't json it in the backend. There's the client-side service