2017-02-06 10 views
1

Я хотел бы знать, можно ли это сделать, потому что я не уверен, что я ошибаюсь или если это невозможно. В принципе, я хочу создать функцию переноса для встроенной функции javascript fetch. Эта функция обертки реализует процесс проверки токена, запрашивая новый accessToken, если тот, который указан, истек и запрашивает снова желаемый ресурс. Это то, что я не достигли до сих пор:Wrap javascript fetch для добавления пользовательских функций

customFetch.js

// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token 
window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    // Try to update 'authorizarion's header in order to send always the proper one to the server 
    options.headers = options.headers || {}; 
    options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

    // Actual server request that user wants to do. 
    const request = window.fetch(url, options) 
     .then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       console.log('not authorized'); 
       return refreshAccesToken(); 
      } 
      else { 
       return d.json(); 
      } 
     }); 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken =() => { 
     window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      // For this example, we can omit this, we can suppose we always receive the access token 
      if (d.status === 401) { 
       // Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       return window.customFetch(url, options, authOptions); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    return request; 
} 

consumer.js

const getResourcePrivate =() => { 
     const url = MAIN_URL + '/resource'; 
     customFetch(url, { 
      method: 'get' 
     },{ 
      url: AUTH_SERVER_TOKEN, 
      unauthorizedRedirect: AUTH_URI, 
      tokenName: TOKEN_NAME 
     }).then((json) => { 
      const resource = json ? json.resource : null; 
      if (resource) { 
       console.log(resource); 
      } 
      else { 
       console.log('No resource has been provided.'); 
      } 
     }); 
} 

Я попытаюсь объяснить немного лучше приведенный выше код: Я хочу сделать прозрачным для пользователей проверку маркера, чтобы позволить им просто беспокоиться о запросе требуемого им ресурса. Этот подход работает нормально, когда accessToken по-прежнему действует, поскольку инструкция return request дает потребителю обещание запроса fetch.

Конечно, когда accessToken истек, и мы запрашиваем новый сервер auth, это не работает. Маркер обновляется и запрашивается частный ресурс, но consumer.js его не видит.

Для этого последнего сценария можно изменить поток программы, чтобы обновить accessToken и выполнить вызов сервера, чтобы снова получить частный ресурс? Потребитель не должен понимать об этом процессе; в обоих случаях (accessToken действителен и accessToken истек и был обновлен) consumer.js должен получить частный запрашиваемый ресурс в своей функции then.

ответ

1

Ну, наконец, я достиг решения. Я попытался разрешить его с помощью Promise, и у него есть работа. Вот подход к customFetch.js файла:

window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    const requestResource = (resolve) => { 
     // Try to update 'authorizarion's header in order to send always the proper one to the server 
     options.headers = options.headers || {}; 
     options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

     window.fetch(url, options) 
      .then((d) => { 
       if (d.status === 401) { 
        // Unauthorized 
        console.log('not authorized'); 
        return refreshAccesToken(resolve); 
       } 
       else { 
        resolve(d.json()); 
       } 
      }); 
    } 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken = (resolve) => { 
     window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       requestResource(resolve); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    let promise = new Promise((resolve, reject) => { 
     requestResource(resolve); 
    }); 

    return promise; 
} 

В принципе, я создал Promise и я назвал внутри функции, которая призывает к серверу, чтобы получить ресурс. Я немного модифицировал request (теперь называется requestResource) и refreshAccessToken, чтобы сделать их параметризуемыми функциями. И я передал им функцию resolve, чтобы «разрешить» любую функцию, когда я получил новый токен.

Возможно, решение может быть улучшено и оптимизировано, но, как первый подход, он работает так, как я ожидал, поэтому я считаю его приемлемым решением.

EDIT: Как мне предложил @Dennis, я допустил ошибку в своем первоначальном подходе. Мне просто нужно было вернуть обещание внутри функции refreshAccessToken, и все будет хорошо. Вот как должен выглядеть файл customFetch.js (что больше похоже на код, который я впервые разместил. Фактически, я только что добавил инструкцию внутри этой функции, хотя бы удаляли и начальные и конечные скобки):

// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token 
window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    // Try to update 'authorizarion's header in order to send always the proper one to the server 
    options.headers = options.headers || {}; 
    options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

    // Actual server request that user wants to do. 
    const request = window.fetch(url, options) 
     .then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       console.log('not authorized'); 
       return refreshAccesToken(); 
      } 
      else { 
       return d.json(); 
      } 
     }); 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken =() => { 
     return window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      // For this example, we can omit this, we can suppose we always receive the access token 
      if (d.status === 401) { 
       // Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       return window.customFetch(url, options, authOptions); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    return request; 
} 
+2

О, я думаю, я уже слишком поздно: DI хотел предложить удалить один набор скобок здесь. Const refreshAccesToken =() => window.fetch (...); 'Этот способ' refreshAccesToken' должен возвращать обещание, вместо 'undefined', и все должно работать нормально –

+0

@ Dennis вы правы!Я сделал ошибку в этой функции, и я забыл вернуть обещание. Я пробовал, и все работает отлично, поэтому я отредактирую свой ответ, чтобы добавить ваш подход, который был еще ближе к моему внутреннему решению. – christiansr85

 Смежные вопросы

  • Нет связанных вопросов^_^