想做一个聊天机器人的界面,后台使用图灵机器人的服务,他们没有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);
小结
无论如何都需要后端配合啊。。有中间层就好多了。