首页 > 解决方案 > 为什么我的微 API 没有响应体?

问题描述

对于我的小型 Javascript 应用程序,我使用CGI编写了服务器端 API 函数。

我做得非常简单,完整的示例脚本如下所示:

#!/usr/bin/env perl

use strict; use warnings; use 5.014; 

use CGI;
use JSON;
use Data::Dumper;

my $q = new CGI;
my %p = $q->Vars;

_api_response();

sub _api_response {
  my ( $error ) = @_;
  my $res;

  my $status = 200;
  my $type = 'application/json';
  my $charset = 'utf-8';

  if ( $error ) {
    $status = 500;
    $res->{data} = {
      status => 500,
    };
    $res->{error} = {
        error => 'failure',
        message => $error,
        detail => Dumper \%p,
    };
  } else {
    $res->{data} = {
      status => 200,
    };
  }

  print $q->header( 
    -status   => $status, 
    -type     => $type,
    -charset  => $charset,
  );

  my $body = encode_json( $res );
  print $body;
}

当我从 JS 脚本调用它时fetch,它没有响应体。如果我从开发者工具/网络检查,它也没有响应正文。如果我在浏览器中输入相同的 URL,它会显示 JSON 正文。如果我curl用作

curl -v 'https://example.com/my_api?api=1;test=2;id=32'

响应似乎也有正确的正文:

< HTTP/2 200 
< date: Mon, 13 Sep 2021 14:04:42 GMT
< server: Apache/2.4.25 (Debian)
< set-cookie: example=80b7b276.5cbe0f250c6c7; path=/; expires=Thu, 08-Sep-22 14:04:42 GMT
< cache-control: max-age=0, no-store
< content-type: application/json; charset=utf-8
< 
* Connection #0 to host example.com left intact
{"data":{"status":200}}

为什么fetch不把它看作一个身体?

为了完整起见,我还包括 JS 部分:

async function saveData(url = '', data = {}) {
  const response = await fetch(url, {
    method: 'GET', 
    mode: 'no-cors', 
    cache: 'no-cache', 
    credentials: 'omit',
    headers: {
      'Content-Type': 'application/json'
    },
    redirect: 'follow', 
    referrerPolicy: 'no-referrer', 
  });
  console.log(response); // body is null
  return response.json(); 
}

将函数用作:

saveData('https://example.com/my_api?api=1;test=2;id=32', { answer: 42 })
  .then(data => {
    console.log(data);
  })
  .catch( error => {
    console.error( error );
  });

在控制台上我看到错误:

SyntaxError: Unexpected end of input

此错误的一个可能原因是空 JSON 字符串。

标签: javascriptapiperlcgifetch-api

解决方案


我能够重现您的问题,然后我能够解决它。

这是一个 CORS 问题。您需要在前端和后端都启用 CORS。

在前端,您需要在页面中使用元标记设置内容安全策略<head>

<meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost">

(不要忘记更改localhost为您的真实域。)

在背面,您需要添加 CORs 标头:

  print $q->header( 
    -status   => $status, 
    -type     => $type,
    -charset  => $charset,
    -access_control_allow_origin => '*', # <-- add this line
  );

作为旁注,您传递的任何设置都不fetch是必需的。而且由于您正在等待响应然后返回另一个承诺,因此实际上没有理由让它成为一个异步函数。

在您准备好对未使用的data参数做某事之前,以下代码就足够了:

function saveData(url = '', data = {}) {
    return fetch(url).then(response=>response.json()); 
}

推荐阅读