hapi,nuxtjs跨域请求

2019-12-03 16:33:49

参考地址 不到50行代码搞懂跨域(CORS)


目标

使用 Hapi 框架在实例 CORS 场景下测试首部字段作用。这里不需要你掌握 Hapi 框架的使用,以及任何 Node 知识。

准备工作

你需要懂得哪方面的知识?

  • 会使用 XMLHttpRequest(Ajax) / Fetch 发起 HTTP 请求

  • 基本的 HTTP 知识

  • 理解基本的 CORS 知识

下面我们会对照这 MDN 上 CORS 部分的讲解(下面简称 讲解),使用具体代码来测试首部字段的作用。

代码部分

主体结构我们按照 Hapi 官网的示例,修改路由部分。

const Hapi = require('@hapi/hapi') const init = async () => {   const server = Hapi.server({     port: 3000,     host: 'localhost'   })   server.route({     method: 'GET',     path: '/',     handler: (request, h) => {       console.log(request.info)       console.log(request.headers)       return {         code: 200,         data: {           success: true         }       }     }   })   await server.start()   console.log('Server running on %s', server.info.uri) } process.on('unhandledRejection', err => {   console.log(err)   process.exit(1) }) init() 复制代码

这样我们就有了一个本地 3000 端口的服务,并有一个 / 路径的 API。下面我们使用 Fetch 发起一个跨域请求,使用 Chrome 打开任意网站,打开 开发者工具,在 Console 下进行测试。

fetch('http://localhost:3000')   .then(function(response) {     return response.json();   })   .then(function(myJson) {     console.log(myJson);   }); 复制代码

收到以下错误:

Access to fetch at 'http://localhost:3000/' from origin 'developer.mozilla.org' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

直接访问 http://localhost:3000 是可以看到结果的。说明浏览器限制从脚本内发起的跨源HTTP请求

下面增加允许跨域源的字段

path: '/', +++ options: { +++   cors: { +++     origin: ['*'] +++   } +++ }, 复制代码

以上代码等同于在 response headers 中增加 access-control-allow-origin 字段为 *,允许任何源的跨域请求。

再次访问,成功获取到内容。对照 MDN 讲解并查看 Chrome Network 面板中 headers 部分和服务日志。

简单请求 与 预检请求

对照讲解理解 “简单请求” 和 ”预检请求“ 的区别。

下面发起一个 POST JSON 的请求,并在服务端接收。

--- method: ['GET'] +++ method: ['GET', 'POST'], // 输出请求体 +++ console.log(request.payload) 复制代码
fetch('http://localhost:3000', { method: 'POST', body: JSON.stringify({'user': 'kenny'}), headers: new Headers({     'Content-Type': 'application/json'   }) }) 复制代码

Network 中会看到有 2 次请求发起,因为我们修改了除规定以外的首部字段,所以首先发起了一个 options 的预检请求。

OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。

同样我们查看 Network 中的 headers 部分进行对比和理解。

带 Cookie 的跨域请求

Fetch 与 CORS 的一个有趣的特性是,可以基于  HTTP cookies 和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

首先增加一个 domain 为 localhost 的 cookie 在测试域下。然后增加下面代码。

// 携带凭证 fetch('http://localhost:3000', {     credentials: 'include' }) 复制代码
origin: ['*'] +++ credentials: true // 输出 cookie +++ console.log(request.state) 复制代码

执行后会发现还是跨域错误。这是因为对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

所以修改 origin

--- origin: ['*'], +++ origin: ['https://developer.mozilla.org'], 复制代码

现在执行,可以在日志中看到刚刚增加的 cookie。

反过来,我们在服务端设置 cookie,看前端能否生效。

h.state('user', 'kenny', {     isSecure: false,     isHttpOnly: false,     isSameSite: 'false',     domain: 'localhost' }) 复制代码

同样可行。

携带额外的首部信息 和 获取前端的首部信息

Access-Control-Allow-Headers 其指明了实际请求中允许携带的首部字段。

Access-Control-Expose-Headers

在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。

首先我们获取服务器时间

先删除多余代码

--- console.log(request.payload) --- console.log(request.state) --- h.state('user', 'kenny', { --- isSecure: false, --- isHttpOnly: false, --- isSameSite: 'false', --- domain: 'localhost' --- }) 复制代码
fetch('http://localhost:3000')   .then(function(response) {     console.log(response.headers.get('Date'))   }) // null 复制代码

后端增加代码,允许获取额外的头部。

+++ additionalExposedHeaders: ['Date'] 复制代码

再次测试后显示接口获取时服务器的时间。

现在前端发送一个自定义的头(Region) 代表当前的地理位置(北京: 52),然后使用后端获取它。

fetch('http://localhost:3000', {     headers: new Headers({         'Region': 52     }) }) 复制代码
+++ additionalHeaders: ['Region'] 复制代码

可以从后端日志中看到 headers 含有 Region 字段

关于 Hapi 框架的 CORS 设置,可以参考:Hapi route cors

全部代码:

const Hapi = require('@hapi/hapi') const init = async () => {   const server = Hapi.server({     port: 3000,     host: 'localhost'   })   server.route({     method: ['GET', 'POST'],     path: '/',     options: {       cors: {         origin: ['https://*.mozilla.org'],         credentials: true,         additionalExposedHeaders: ['Date'],         additionalHeaders: ['Region']       }     },     handler: (request, h) => {       console.log(request.info)       console.log(request.headers)       console.log(request.payload)       console.log(request.state)       // 设置 cookie       // h.state('user', 'kenny', {       //   isSecure: false,       //   isHttpOnly: false,       //   isSameSite: 'false',       //   domain: 'localhost'       // })       return {         code: 200,         data: {           success: true         }       }     }   })   await server.start()   console.log('Server running on %s', server.info.uri) } process.on('unhandledRejection', err => {   console.log(err)   process.exit(1) }) init() 复制代码

结语

跨域问题其实并不复杂,网上教程也非常多,其实对于同源策略和跨域的概念,只要阅读 MDN 就可以了,自己动手创建一个服务器,对照调试工具和后端日志,查看 HTTP 请求和响应,加深理解。希望这个实例教程能帮助大家理解前端跨域 和 Hapi 框架的使用。

附上一个 整理和机翻的 Hapi 中文文档,觉得有用的小伙伴可以点点关注。



  • 2020-03-14 23:39:59

    vuetify和@nuxt/vuetify icon 之我见

    vuetify中v-icon,貌似默认支持 Material Design Icons, Material Icons, Font Awesome 4 and Font Awesome 5, 我自己单独引入了vuetify 用哪一个图标都没有问题。但是用了@nuxt/vuetify只能用mdi-home这样的。不知道因为啥。肯定是封装后,封装成一个了。 但是我修改vuetify的设置,哪一个图标也都能用。哎,不过多研究了。

  • 2020-03-16 15:57:53

    nuxtjs中单独引入Message组件的问题

    // 引入elementUIimport { Message } from 'element-ui';//由于Message组件并没有install 方法供Vue来操作的,是直接返回的,因此按照官方文档单独引入的方法是//会报错的,需要给 Message 添加 install 方法Message.install = function (Vue, options) {Vue.prototype.$message = Message}Vue.use(Message )//消息提示

  • 2020-03-16 16:03:20

    css的var()函数

     随着sass,less预编译的流行,css也随即推出了变量定义var函数。var()函数,就如同sass和less等预编译软件一样,可以定义变量并且进行对应的使用。

  • 2020-03-16 16:52:05

    对icomoon的误解,以及最快速的使用

    此时需要注意顶部第一个选项,Quick Usage,一定要打开,Enable Quick Usage,谁让咱英语不好呢,这个时候会出现一个css连接,直接引用就好了,就可以随意使用图标了,引入这一个css就能实现我们的功能,省区引入太多文件的烦恼,你可以在浏览器打开这个css,可以看到里面把我们所用的文件整成base64了。所以挺好用的。

  • 2020-03-17 09:47:05

    video标签视频不自动播放的问题

    添加 muted 属性,就可以通过地址栏进入网页的时候自动播放了,手机端还是有的有限制的,比如iphone浏览器,就不行,苹果手机为了保护用户的流量和用户的意愿,是禁止自动播放的,必须有手动触发。

  • 2020-03-17 14:21:31

    nuxt+pm2 自动化部署及打包后文件自动上传阿里云 oss(精华)

    部署nuxtjs,这一篇文章就够了,pm2 代码自动发布依赖于 git 工具,先将 ssh 密钥配置再你的代码仓库(github 或者 gitLab),具体操作自行 google 或者点击github 配置 ssh。 使用 ssh 密钥链接服务器 s $ ssh-copy-id root@1.2.3.4 # 把本机的 SSH 秘钥添加至服务器,配置成功后,以后就不需要再执行这条 SSH 命令了