想做一个聊天机器人的界面,后台使用图灵机器人的服务,他们没有demo,遂自己写一个post请求。然而由于同源策略(CORS,Cross-Origin Sharing Standard),这个唯一的api完全不能用。

跨域请求

实现跨域请求的方式很多,但纯前端实现有点困难,比较简单的实现有两种,都需要服务器配合。

JSONP

我们知道CORS会阻止从JS代码访问其他网站的行为,但像<img><script>一类的标签允许从其他网站加载资源。利用<script>的这一特性,我们将服务器返回的JSON数据包装成JS代码,就能够实现跨域请求。以JSONP实现的跨域请求都是GET请求。

常用的http库中axios不支持jsonp,我选择了vue-jsonp。该依赖会将jsonp格式的数据解释成json。

this.$jsonp("http://api.guohere.com/api.php", {
        text: this.text
      }).then(res => {
        console.log(res);
        this.text=res;
      }).catch(err=>{
        console.log(err);
      });

JSONP的后端配置

如果后端以JSON格式输出,会触发CORB(Cross-Origin Read Blocking),这是因为浏览器会认为传输的内容不是JS代码(而你借用了<script>标签的开放性)。传输与限定格式不符的内容是一个具有潜在隐患的行为,尽管非JS内容不会运行,<img>引入的非图片资源也不会加载,但这些资源会留在当前页面进程使用的内存空间里。

幽灵和熔断漏洞和CPU预执行有关,程序存在访问超出边界的内存空间的机会,因此有必要阻止不符合预期的内容。CORS仅仅不加载内容,CORB甚至不会读取内容到内存。更多关于CORB的内容可以访问https://segmentfault.com/a/1190000016126079

说这么多,我们要做的就是将JSON包装成JS代码,这里把json作为callback函数的参数,最后的输出就是jsonp的格式。

$jsoncallback = htmlspecialchars($_REQUEST ['callback']);
echo $jsoncallback . "(" . $response . ")";
//返回$jsoncallback([json_content])

CORS

CORS规定,如果服务器的响应头中指定了Access-Control-Allow-Origin,那么该属性规定的域名可以向当前服务器发起请求(域名可以是通配符)。

在PHP中,可以这样描述:

header("Access-Control-Allow-Origin:http://abc.com:8080");

如果需要发送cookie,则不能使用通配符,且只能指定一个站点。

后端配置

api.php应当接受参数并且向真正的api地址发起请求,这里不考虑cookie,简单实现了一下(实际上在使用postman实验时发现它可以直接生成各种语言发起请求的代码)。

$text = $_GET["text"];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "http://openapi.tuling123.com/openapi/api/v2",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\n        reqType: 0,\n        perception: {\n          inputText: {\n            text: \".$text.\"\n          },\n               },\n        userInfo: {\n          apiKey: \"14322298e9b54342bbcb56249595122b\",\n          userId: \"******\"\n        }\n      }",
  CURLOPT_HTTPHEADER => array(
    
    "cache-control: no-cache"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

小结

无论如何都需要后端配合啊。。有中间层就好多了。