Опубликован: 27.01.2016 | Доступ: свободный | Студентов: 914 / 58 | Длительность: 23:07:00
Лекция 9:

Обновление, демонстрация и удаление пользователей

Дружелюбная переадресация

Защита наших страниц закончена, как и было написано, но есть один небольшой недостаток: когда пользователи пытаются получить доступ к защищенной странице, они перенаправляются к странице профиля, независимо от того, куда они пытались попасть. Другими словами, если не залогинившийся пользователь пытается посетить страницу редактирования, после входа пользователь будет перенаправлен на /users/1 вместо /users/1/edit. Было бы намного более доброжелательно, по отношению к пользователям, все же перенаправлять их на запрашиваемую страницу вместо этого .

Для того чтобы протестировать такую "дружелюбную переадресацию", мы вначале посещаем страницу редактирования пользователя, которая перенаправит нас на страницу входа. Затем мы введем валидную информацию для входа и кликнем по кнопке "Sign in". Результирующей страницей, которой по дефолту является профиль пользователя, в данном случае должна быть страница "Edit user". Тест для этой последовательности представлен в Листинге 9.16.

require 'spec_helper'

describe "Authentication" do
  .
  .
  .
  describe "authorization" do

    describe "for non-signed-in users" do
      let(:user) { FactoryGirl.create(:user) }

      describe "when attempting to visit a protected page" do
        before do
          visit edit_user_path(user)
          fill_in "Email",    with: user.email
          fill_in "Password", with: user.password
          click_button "Sign in"
        end

        describe "after signing in" do

          it "should render the desired protected page" do
            expect(page).to have_title('Edit user')
          end
        end
      end
      .
      .
      .
    end
    .
    .
    .
  end
end
Листинг 9.16. Тест для дружелюбной переадресации. spec/requests/authentication_pages_spec.rb

Теперь реализация.4Код в этом разделе это адаптация (переделка) гема Clearance команды разработчиков thoughtbot Для того чтобы перенаправить пользователей к запрашиваемой ими странице, нам нужно где-то сохранить запрашиваемую страницу, а затем переадресовать к ней. Мы добьемся этого с помощью пары методов, store_location и redirect_back_or, определенных в хелпере Sessions (Листинг 9.17).

module SessionsHelper
  .
  .
  .
  def redirect_back_or(default)
    redirect_to(session[:return_to] || default)
    session.delete(:return_to)
  end

  def store_location
    session[:return_to] = request.url if request.get?
  end
end
Листинг 9.17. Код реализующий дружественную переадресацию. app/helpers/sessions_helper.rb

В роли механизма хранения выступает объект session предоставляемый Rails, о котором вы можете думать как об экземпляре переменной cookies из Раздела 8.2.1 которая автоматически истекает при закрытии браузера. Мы также используем объект request для получения url, т.e. URL запрашиваемой страницы. Метод store_location помещает запрашиваемый URL в переменную session под ключом :return_to, но только для GET запроса (if request.get?). Это предотвращает сохранение URL для перенаправления если пользовател, скажем, отправляет форму не будучи залогиненым (что, пусть и является крайним случаем, но все же может случиться если, например, пользователь удалил remember token вручную перед отправкой формы); в данном случае, результирующий редирект выдаст GET запрос к URL ожидающему POST, PATCH или DELETE, что приведет к ошибке.5

Для того чтобы использовать store_location, нам необходимо добавить ее в предфильтр signed_in_user, как это показано в Листинге 9.18.

class UsersController < ApplicationController
  before_action :signed_in_user, only: [:edit, :update]
  before_action :correct_user,   only: [:edit, :update]
  .
  .
  .
  def edit
  end
  .
  .
  .
  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end

    # Before filters

    def signed_in_user
      unless signed_in?
        store_location
        redirect_to signin_url, notice: "Please sign in."
      end
    end

    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end
end
Листинг 9.18. Добавление store_location в предфильтр :signed_in_user. app/controllers/users_controller.rb

Для того чтобы реализовать саму переадресацию, мы используем метод redirect_back_or для перенаправления на запрашиваемый URL если он существует или на какой-либо дефолтный URL в противном случае, который мы добавим в действие create контроллера Sessions для переадресации после успешного входа (Листинг 9.19). Метод redirect_back_or использует оператор "или" ||

session[:return_to] || default

Этот код оценивает session[:return_to] и до тех пор, пока оно не является nil, в противном случае он оценивает заданный дефолтный URL. Обратите внимание, что Листинг 9.17 заботится об удалении URL перенаправления; в противном случае, последующие попытки входа перенаправлялись бы на защищенную страницу до тех пор, пока пользователь не закроет браузер. (Тестирование этого поведения оставлено в качестве упражнения (Раздел 9.6.)

class SessionsController < ApplicationController
  .
  .
  .
  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      sign_in user
      redirect_back_or user
    else
      flash.now[:error] = 'Invalid email/password combination'
      render 'new'
    end
  end
  .
  .
  .
end
Листинг 9.19. Действие create контроллера Sessions с дружелюбной переадресацией. app/controllers/sessions_controller.rb

(Если вы выполнили первое упражнение в Главае 8, убедитесь в том что вы используете правильный хэш params в Листинге 9.19.)

С этим интеграционные тесты дружелюбной переадресации из Листинга 9.16 должны пройти и базовая аутентификация и защита страниц могут считаться законченными. Как обычно, прежде чем продолжать, хорошо бы проверить что набор тестов зеленый:

$ bundle exec rspec spec/
Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?