首頁  >  文章  >  web前端  >  透過Koa2框架利用CORS實現跨域ajax請求的方法

透過Koa2框架利用CORS實現跨域ajax請求的方法

亚连
亚连原創
2018-06-01 11:17:231563瀏覽

這篇文章主要介紹了淺談Koa2框架利用CORS完成跨域ajax請求,現在分享給大家,也給大家做個參考。

實現跨域ajax請求的方式有很多,其中一個是利用CORS,而這個方法關鍵是在伺服器端進行設定。

本文僅對能夠完成正常跨域ajax回應的,最基本的配置進行說明(深層的配置我也不會)。

CORS將請求分為簡單請求和非簡單請求,可以簡單的認為,簡單請求就是沒有加上額外請求頭部的get和post請求,並且如果是post請求,請求格式不能是application /json(因為我對這一塊理解不深如果錯誤希望能有人指出錯誤並提出修改意見)。而其餘的,put、post請求,Content-Type為application/json的請求,以及自訂的請求頭部的請求,就為非簡單請求。

簡單請求的配置十分簡單,如果只是完成回應就達到目的的話,只需配置回應頭部的Access-Control-Allow-Origin即可。

如果我們在http://localhost:3000 網域下想要造訪 http://127.0.0.1:3001 網域。可以做如下設定:

app.use(async (ctx, next) => {
 ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000');
 await next();
});

然後用ajax發起一個簡單請求,例如post請求,就可以輕鬆的得到伺服器正確回應了。

實驗程式碼如下:

$.ajax({
  type: 'post',
  url: 'http://127.0.0.1:3001/async-post'
 }).done(data => {
  console.log(data);
})

伺服器端程式碼:

router.post('/async-post',async ctx => {
 ctx.body = {
 code: "1",
 msg: "succ"
 }
});

然後就能得到正確的回應訊息了。

這時候如果看一下請求和回應的頭部訊息,會發現請求頭部多了個origin(還有一個referer為發出請求的url位址),而回應頭部多了個Access- Control-Allow-Origin。

現在可以發送簡單請求了,但是要想傳送非簡單請求還是需要其他的設定。

當第一次發出非簡單請求的時候,實際上會發出兩個請求,第一次發出的是preflight request,這個請求的請求方法是OPTIONS,這個請求是否通過決定了這一個種類的非簡單請求是否能成功得到回應。

為了能在伺服器匹配到這個OPTIONS類型的請求,因此需要自己做一個中間件來進行匹配,並給出回應使得這個預檢能夠通過。

app.use(async (ctx, next) => {
 if (ctx.method === 'OPTIONS') {
 ctx.body = '';
 }
 await next();
});

這樣OPTIONS請求就能夠通過了。

如果檢查一下preflight request的請求頭部,會發現多了兩個請求頭。

Access-Control-Request-Method: PUT
Origin: http://localhost:3000

要透過這兩個頭部資訊與伺服器進行協商,看是否符合伺服器應答條件。

很容易理解,既然請求頭多了兩個訊息,回應頭自然也應該有兩個訊息相對應,這兩個訊息如下:

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: PUT,DELETE,POST,GET

第一個訊息和origin相同因此通過。第二個訊息對應Access-Controll-Request-Method,如果在請求的方式包含在伺服器允許的回應方式之中,因此這條也通過。兩個約束條件都滿足了,所以可以成功的發起請求。

至此為止,相當於僅僅完成了預檢,還沒發送真正的請求。

真正的請求當然也成功獲得了回應,並且回應頭如下(省略不重要部分)

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: PUT,DELETE,POST,GET

請求頭如下:

Origin: http://localhost:3000

這就很顯而易見了,回應頭部資訊是我們在伺服器設定的,因此是這樣。

而客戶端因為剛才已經預先檢查過了,所以不需要再發Access-Control-Request-Method這個請求頭了。

這個範例的程式碼如下:

$.ajax({
   type: 'put',
   url: 'http://127.0.0.1:3001/put'
  }).done(data => {
   console.log(data);
});

伺服器程式碼:

app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000');
  ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
  await next();
});

至此我們完成了能夠正確進行跨網域ajax回應的基本配置,還有一些可以進一步配置的東西。

例如,到目前為止,每一次非簡單請求都會實際上發出兩次請求,一次預檢一次真正請求,這就比較損失性能了。為了能不發預檢請求,可以設定如下回應頭。

Access-Control-Max-Age: 86400

這個回應頭的意義在於,設定一個相對時間,在該非簡單請求在伺服器端通過檢驗的那一刻起,當流逝的時間的毫秒數不足Access-Control-Max-Age時,就不需要再進行預檢,可以直接發送一次請求。

當然,簡單請求時沒有預檢的,因此這條程式碼對簡單請求沒有意義。

目前程式碼如下:

app.use(async (ctx, next) => {
 ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000');
 ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
 ctx.set('Access-Control-Max-Age', 3600 * 24);
 await next();
});

到現在為止,可以對跨網域ajax請求進行回應了,但是該網域下的cookie不會被攜帶在請求頭中。如果想要帶著cookie到伺服器,並且允許伺服器對cookie進一步設置,還需要進一步的配置。

為了方便後續的偵測,我們預先在http://127.0.0.1:3001這個網域下設定兩個cookie。注意不要錯誤把cookie設定成中文(剛才我就設定成了中文,結果報錯,半天沒找到出錯原因)

然後我們要做兩步,第一步設定回應頭Access-Control-Allow -Credentials為true,然後在客戶端設定xhr物件的withCredentials屬性為true。

客戶端程式碼如下:

$.ajax({
   type: 'put',
   url: 'http://127.0.0.1:3001/put',
   data: {
    name: '黄天浩',
    age: 20
   },
   xhrFields: {
    withCredentials: true
   }
  }).done(data => {
   console.log(data);
  });

服務端如下:

app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000');
  ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
  ctx.set('Access-Control-Allow-Credentials', true);
  await next();
});

这时就可以带着cookie到服务器了,并且服务器也可以对cookie进行改动。但是cookie仍是http://127.0.0.1:3001域名下的cookie,无论怎么操作都在该域名下,无法访问其他域名下的cookie。

现在为止CORS的基本功能已经都提到过了。

一开始我不知道怎么给Access-Control-Allow-Origin,后来经人提醒,发现可以写一个白名单数组,然后每次接到请求时判断origin是否在白名单数组中,然后动态的设置Access-Control-Allow-Origin,代码如下:

app.use(async (ctx, next) => {
 if (ctx.request.header.origin !== ctx.origin && whiteList.includes(ctx.request.header.origin)) {
  ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin);
  ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
  ctx.set('Access-Control-Allow-Credentials', true);
  ctx.set('Access-Control-Max-Age', 3600 * 24);
 }
 await next();
});

这样就可以不用*通配符也可匹配多个origin了。

注意:ctx.origin与ctx.request.header.origin不同,ctx.origin是本服务器的域名,ctx.request.header.origin是发送请求的请求头部的origin,二者不要混淆。

最后,我们再稍微调整一下自定义的中间件的结构,防止每次请求都返回Access-Control-Allow-Methods以及Access-Control-Max-Age,这两个响应头其实是没有必要每次都返回的,只是第一次有预检的时候返回就可以了。

调整后顺序如下:

app.use(async (ctx, next) => {
 if (ctx.request.header.origin !== ctx.origin && whiteList.includes(ctx.request.header.origin)) {
  ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin);
  ctx.set('Access-Control-Allow-Credentials', true);
 }
 await next();
});

app.use(async (ctx, next) => {
 if (ctx.method === 'OPTIONS') {
  ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
  ctx.set('Access-Control-Max-Age', 3600 * 24);
  ctx.body = '';
 }
 await next();
});

这样就减少了多余的响应头。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

webpack打包js的方法

vue 简单自动补全的输入框的示例

angular5 httpclient的示例实战

以上是透過Koa2框架利用CORS實現跨域ajax請求的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn