JS: Как передать URL через функцию перенаправления в функцию входа

В моем приложении React/nextJS я проверяю действительный токен в статической функции getInitialProps. Я использую это как HOC - но это не должно иметь значения в этом случае.

Если токен недействителен (или отсутствует), пользователь перенаправляется на страницу входа. Это делается с помощью функции redirect как показано ниже. Все идет нормально.

Как я могу передать URL страницы, с которой пользователь перенаправляется в компонент входа?

Если пользователь не вошел в систему и вызывает что-то вроде http://my-server.com/any-page, он перенаправляется на страницу индекса (http://my-server.com): будет вход в систему форма. Если вход выполнен успешно, я бы хотел перенаправить его обратно на первую вызванную страницу: http://my-server.com/any-page

  1. Вызовите страницу с ограниченным доступом как не авторизованный пользователь
  2. Перенаправить на главную страницу входа
  3. После авторизации перенаправить обратно на страницу 1.

Я понятия не имею, как передать эту информацию в функцию входа в систему...

с-сервера-props.js

export default WrappedComponent =>
  class extends Component {
    static async getInitialProps (context) {
      const { req, pathname } = context
      let isValid = false

      if (req && req.headers) {
        const cookies = req.headers.cookie
        if (typeof cookies === 'string') {
          const cookiesJSON = jsHttpCookie.parse(cookies)
          initProps.token = cookiesJSON['auth-token']
          if (cookiesJSON['auth-token']) {
            jwt.verify(cookiesJSON['auth-token'], secret, (error, decoded) => {
              if (error) {
                console.error(error)
              } else {
                isValid = true
              }
            })
          }
        }
      }

      // Redirect to index (=login) page if isValid is false
      if (!isValid && pathname && pathname !== '/') {
        redirect(context, pathname ? '/?ref=' + pathname : '/')
      }

      return initProps
    }
    render () {
      return <WrappedComponent {...this.props} />
    }
  }

redirect.js

import Router from 'next/router'

export default (context, target) => {
  if (context.res) {
    // server
    context.res.writeHead(303, { Location: target })
    context.res.end()
  } else {
    // In the browser, we just pretend like this never even happened ;)
    Router.replace(target)
  }
}

страниц/index.js

В index.js есть функция submit для входа в систему пользователя. Там пользователь должен быть перенаправлен на начальную страницу:

_onSubmit (event) {
  this.props.loginMutation({
    variables: { username, password }
  }).then(response => {
    const token = response.data.token
    if (token) {
      Cookies.set('auth-token', token, { expires: 1 })
      this.props.client.resetStore().then(() => {
        window.location.assign('/') // <-- Redirect to initial called page
      })
    }
  })
}

Ответ 1

В вашем with-server-props.js замените путь на объект URL

redirect(context, {
    pathname: '/',
    query: { redirect: req.url } // req.url should give you the current url on server side
  })

это добавит параметр перенаправления в URL https://example.com/?redirect=/about

тогда вы можете получить параметры URL на любой странице, используя getInitialProps:

this.redirectUrl = (req && req.query['redirect']) ? decodeURIComponent(req.query['redirect']) : '/'

в конце концов

window.location.assign(this.redirectUrl)

надеюсь, это поможет, дайте мне знать.

Ответ 2

Что вам нужно, это react-router или, более конкретно, пакет react-router-dom. Это бриз, если вы понимаете, как это работает.

Для вашего сценария вместо вызова redirect() когда он не аутентифицирован, вы используете <Redirect to={url}/>. Это автоматически заменяет URL-адрес браузера и обновляет глобальное состояние. Тем не менее, вы уже аккуратно назначили URL-адрес, соответствующий любому особому случаю, который нужно перехватить. Например, "/login/ref/: toref" будет базовым выражением для обработки URL "/login/ref/{specialaccess}".

Обратите внимание ":". Это сопоставление параметров и необходимо для получения URL-адреса в компоненте входа в систему.

Как говорится, строка кода стоит тысячи слов. Поэтому я сделал небольшой проект, чтобы в полной мере продемонстрировать, как можно реализовать некоторые важные функции react-router-dom.

Найти здесь: https://codesandbox.io/s/y0znxpk30z

В проекте, когда вы пытаетесь получить доступ к https://y0znxpk30z.codesandbox.io/specialaccess через симулятор браузера, вы перенаправляетесь на страницу специального доступа после прохождения аутентификации при входе в систему. В противном случае, если вы заходите на https://y0znxpk30z.codesandbox.io, после входа в систему вы будете перенаправлены на домашнюю страницу.

Помните, вы должны обернуть любой компонент, который ожидает глобальный реквизит, с помощью withRouter следующим образом:

export default withRouter(component-name);

Это обеспечивает this.props.location, this.props.history и this.props.match в каждом компоненте, поскольку мы уже поместили корневой компонент приложения в <BrowserRouter><BrowserRouter/> доступный по умолчанию из пакета.

С помощью this.props.match мы можем легко ссылаться и перенаправлять на URL, который мы указали в ": toref" ранее.

Вы можете прочитать больше о react-router здесь

Ответ 3

Функция jwt.verify используется в асинхронном режиме обратного вызова.

Этот стиль более подходит для componentDidMount метод Жизненный цикл WrappedComponent используется таким образом.
Передача обратного вызова для него означает, что значение для isValid никогда не может быть обновлено достаточно рано, даже если клиентский токен JWT является допустимым и пользователь всегда будет перенаправлен.

Я предлагаю использовать синхронный вариант без обратного вызова (проверьте, сколько времени прошло до того, как будет обработан упакованный компонент). Более того, преобразуйте jwt.verify обратного вызова jwt.verify в функцию, которая возвращает обещание, чтобы его можно было разрешить в выражении await учитывая, что getInitialProps является async функцией.

if (req && req.headers) {
  const cookies = req.headers.cookie
  if (typeof cookies === 'string') {
    const cookiesJSON = jsHttpCookie.parse(cookies)
    initProps.token = cookiesJSON['auth-token']
    if (cookiesJSON['auth-token']) {
      try {
         const payload = jwt.verify(cookiesJSON['auth-token'], secret)
         if (payload) {
            isValid = true
         }
      } catch (err) {
         isValid = false
      }
    }
  }
}

Теперь в методе _onsubmit для перенаправления пользователя вы можете получить значение параметра запроса ref установленное в WrappedComponent.getInitialProps и использовать его для перенаправления пользователя.

const ref = new URLSearchParams(location.search).get('ref');
location.assign('/${ref}');

Ответ 4

Передайте возвращаемый URL либо в качестве параметра запроса, либо в качестве состояния местоположения на страницу входа. Я нашел пример на странице документации для Next.js по проталкиванию маршрута с параметрами запроса.

import Router from 'next/router'

const handler = () => {
  Router.push({
    pathname: '/about',
    query: { name: 'Zeit' }
  })
}

export default () => (
  <div>
    Click <span onClick={handler}>here</span> to read more
  </div>
)

Вместо Router.replace, попробуйте Router.push с обратным URL-адресом, переданным из HOC. Надеюсь это поможет.