1. 程式人生 > >Next.js中併發時傳遞cookie錯亂的解決辦法

Next.js中併發時傳遞cookie錯亂的解決辦法

一、問題描述

    最近在開發專案時遇到了一個問題,我們使用cookie儲存登入資訊,當用戶已經登入過我們的網站(儲存了cookie),再次進入網站後可能顯示別人的賬戶資訊,重新整理一下才能正確顯示自己的登入資訊。

    經過折磨人的測試才重現了場景,發現是當兩人或以上同時訪問網站時,有一個人的cookie會把另一個人的cookie覆蓋,就導致了被覆蓋的這個人的賬戶資訊出了問題。

二、產生原因

    問題出現在服務端請求中,在修改之前專案中的server.js檔案裡關於cookie是這樣寫的:

...
server.get('*', (req, res) => {
    axios.defaults.headers.Cookie = req.headers.cookie||"";
    ...
    axios.post('/api/config/findSeoConfig',{'keyName':'seo_mall'}).then((response){
        ...
    }
    ...
}
...

     首先在headers裡設定了一個預設Cookie,但是這裡Cookie是全域性的,下面發axios封裝過的post請求就會直接取這個全域性的Cookie,但是Next裡沒有執行緒鎖的功能,當併發操作時,它不會讓一個執行緒執行時另一個執行緒處於阻塞狀態,而是讓多個使用者的非同步請求同時執行,所以誰後進就會把先進的cookie給覆蓋掉,所以造成了混亂。

三、解決辦法

    取消全域性Cookie,給服務端的每一個請求都單獨加入cookie

...
server.get('*', (req, res) => {
    //axios.defaults.headers.Cookie = req.headers.cookie||"";
    axios.defaults.withCredentials = true;//設定跨域請求時需要使用憑證
    ...
    axios({
          headers: {cookie: req.headers.cookie},
          url: '/api/config/findSeoConfig',
          method: 'post',
          data: {'keyName' : 'seo_mall'},
      }).then((response) => {
        ...
    }
    ...
}
...

    Next的page中的getInitialProps()方法中發的dispatch也屬於服務端請求,也需要單獨新增cookie

...
static async getInitialProps(props) {
    const {store, req} = props;
    await store.dispatch({
        type: 'mallTopAd/getMallTopAd',
        payload: {
          cookie: req.headers.cookie,
        }
    });
    ...
}
...

    model層和servcie層就不多加贅述

//model層
...
effects: {
        * getMallTopAd({payload}, {call, put}) {
                ...
            }
        ...
        }
...

//service層
...
export function fetchMallTopAd(params) {
    return request({
        url: mallTopAd,
        method: 'post',
        data: params,
    })
}
...

     service中的request是自己封裝的處理髮axios的方法,其實本質做法就是把發的axios請求的headers中加入Cookie。

//utils/request.js
...
const fetchData = (options) => {
  let {
    method = 'get',
    data,
    url,
  } = options;
  //如果cookie存在,執行下面switch的預設方法
  if(data && data.cookie){
    options.headers = {Cookie: data.cookie};
    method = '';
    delete data.cookie;//傳入後臺介面中的資料除去cookie
  }
  const cloneData = lodash.cloneDeep(data);
  switch (method.toLowerCase()) {
    case 'get':
      return axios.get(url, {
        params: cloneData,
      })
    case 'delete':
      return axios.delete(url, {
        data: cloneData,
      })
    case 'post':
      return axios.post(url, cloneData)
    case 'put':
      return axios.put(url, cloneData)
    case 'patch':
      return axios.patch(url, cloneData)
    default:
      return axios(options)
   }
}

export default function request (options) {
    return fetchData(options).then((response) => {
        ...
    }
}

    經過這樣的處理,所有的服務端請求都會執行default中的axios,類似於上面寫的server.js中的處理。不直接在‘post’、‘get’等中直接新增headers是因為改動大比較麻煩,所以都放在default中處理了。headers中不僅能改Cookie,還能改Content-Type等,而且寫法也比較靈活,我這裡只是專案裡用的封裝的request才略顯笨重。坑還是要慢慢爬。

四、總結

    總而言之言而總之,給每一個服務端請求加上Cookie就完事兒了。我們這個問題是個線上bug,被Next安排的明明白白。