Может войдёшь?
Черновики Написать статью Профиль

Основы Laravel 5: Посредники - почти что огры

перевод Основы Laravel 5 Laracasts

Это перевод видео-урока с Laracasts, серия Laravel 5 Fundamentals, урок №16Ogres Are Like Middleware от . Перевод обновлён . Опечатка? Выдели и нажми Ctrl+Enter.

(0:00)
Давайте поговорим немного о посредниках. Итак, представьте, что мы закончили наш сайт и выложили в производство. Может ли кто угодно зайти на эту страницу (articles)? Конечно. Без проблем. А как насчет страницы с самой статьёй? Опять же, никаких проблем нет. Но как насчёт страницы для создания статьи? Мы точно не хотим чтобы кто угодно мог иметь доступ к этой странице. Это должно быть исключительно для тех людей, кто зарегистрировался и вошёл в систему. Так что похоже что нам нужно каким-то образом сказать:

(0:30)
«Если вы просто гость на моём сайте, у вас нет разрешения на создание новой статьи». Что ж, позвольте мне показать вам, как это сделать, и в процессе вы узнаете о понятии промежуточного программного обеспечения – посредников (middleware). ОК, так что давайте переключимся и откроем наш PHPArticlesController. И если мы посмотрим на метод PHPcreate(), действительно, вы могли бы оградить его здесь. Я имею в виду, что мы могли бы сказать что-то вроде этого:

PHP
if (Auth::guest())

(если посетитель просто гость, то он не имеет разрешения быть здесь).

(1:00)
Так что мы перенаправим его обратно на страницу articles:

PHP
return redirect('articles');

Да, правда, мы могли бы так сделать. Так что, если мы обновим эту страницу, мы вошли в систему, и значит у нас есть доступ. Но если мы выйдем из системы, и кстати не забывайте, что у нас ещё нет страницы home. Но в любом случае, если мы вернёмся к articles/create, а у нас нет разрешения, то нас просто вернут обратно на articles. Так что да, это действительно работает. Но, как вы можете себе представить, для типичного приложения, вам понадобится похожая логика во многих, многих местах.

(1:30)
Так что было бы досадно, если бы нам пришлось переписывать такого рода логику снова и снова. К счастью, есть способ получше. Во-первых, я собираюсь показать вам некоторые встроенные возможности для достижения того же эффекта. И затем мы поговорим немного больше о посредниках. Итак, если я открою боковую панель, и перейду к PHPHttp/Middleware, вы увидите, что у нас здесь уже предустановлены два посредника. И заметьте, у них хорошо читаемые имена: PHPAuthenticate и PHPRedirectifAuthenticated.

(2:00)
Ну, для начала, давайте просто посмотрим что происходит, и затем мы медленно начнём рисовать большую картину. Итак, мы внедряем наш аутентификатор. И теперь в рамках метода PHPhandle(), где мы обрабатываем входящий запрос, мы поставим «стражей» (guards). В этом случае, назовём его методом проверки PHPcheck(). Это кстати очень похоже на исполнение PHPAuth::check(). Мы просто используем внедрение зависимостей, а не фасады Laravel, которые хороши во многих случаях. Как бы то ни было, если мы вызовем PHPAuth::сheck(), думайте об этом, как о способе сказать:

(2:30)
«Вошли ли вы в систему? Проверьте и убедитесь что пользователь вошёл». И если это так, то эта страница должна быть доступна только для гостей. Таким образом, мы перенаправим вас, в этом случае, на home. Теперь, вы можете подумать: «Когда бы это было полезно?». Ну, как насчёт такого примера? Подумайте о сайте типа Laracasts. Если вы вошли в систему, имеет ли смысл, давать вам доступ к странице регистрации? Нет, это не имеет смысла.

(3:00)
У вас уже есть учетная запись, поэтому вам не нужно снова регистрироваться. Таким образом, мы могли бы использовать этого посредника на данном маршруте. И мы могли бы присоединить этого посредника к любому подобному маршруту. Если вы вошли в систему, то нет, вам не нужно видеть эту страницу. Вместо этого я перенаправлю вас в dashboard. Теперь, вот этот следующий, PHPAuthenticate, он вроде как противоположный. На этот раз, когда мы обрабатываем запрос, и обратите внимание, что определение в точности такое же. Так работают посредники.

(3:30)
Думайте о них как о конвейерах или трубах (pipes), или даже о маленьких кусочках лука. Когда мы добираемся до центра нашего лука, у нас есть все эти маленькие слои, у которых есть возможность ответить на запрос. И даже полностью отклонить или предотвратить его. Поэтому на этот раз мы говорим:

PHP
If ($this->auth->guest())

Если пользователь является гостем, то у него нет доступа к этой странице. И затем мы просто делаем быструю проверку. Если делается AJAX-запрос, то возвращаем ответ об отсутствии прав, иначе, перенаправляемся на страницу входа в систему.

(4:00)
Так что на самом деле, это именно то, что мы хотим сделать в этом случае. Если мы хотим защитить эту форму для создания новой статьи, то мы можем присоединить этого посредника. Давайте посмотрим, как мы можем воспользоваться им. Если я перейду к PHPHttp/Kernel, вот где мы можем зарегистрировать любого посредника. И вы увидите, что это очень простой класс. Большая часть работы здесь уже делается в пределах родительского класса. Но для нас, мы можем полностью сосредоточиться на этих двух массивах. Теперь, в чём тут разница?

(4:30)
Для этого, первого, любой посредник, которого мы добавим к этому массиву, будет запускаться для каждого запроса. И заметьте, что Laravel здесь уже делает кучу всего. Но дальше, у нас тут есть PHP$routeMiddleware. Это только для случаев, когда мы хотим присоединить посредников к конкретным маршрутам. Так что, вы можете думать об этом как о выборочном подходе. Теперь заметьте, что эти два здесь мы уже рассмотрели. Но что насчёт этих ключей auth и guest' Думайте о них, как о коротких идентификаторах для посредников.

(5:00)
Теперь, если мы вернёмся к PHPArticlesController и мы тут хотим добавить, в этом случае, посредник PHPAuthenticate, то вот как мы можем это сделать. Я создам здесь конструктор и скажу:

PHP
$this->middleware('auth');

И мы будем ссылаться на этот ключ там. Ещё раз, чтобы навсегда запомнить это – вот это значение ссылается на этот ключ здесь. Так что я говорю, что для всего этого контроллера, я хочу вызывать посредника PHPAuthenticate.

(5:30)
Хорошо, давайте посмотрим, как это работает. Давайте обновим. Запустим. Мы не вошли в систему, поэтому нас автоматически перенаправляют на форму входа в систему. И это будет справедливо для всего прочего. При переходе к articles/create или чтобы просмотреть конкретную статью, посредник присоединяется к каждому отдельному маршруту, потому что мы установили его таким образом. Но в некоторых ситуациях, вы не захотите применять его к каждому отдельному маршруту или методу в контроллере, может вам только нужно позаботиться об одном или двух маршрутах.

(6:00)
Тогда мы можем сделать это следующим образом:

PHP
$this->middleware('auth' , ['only' => 'create']);

Запустить этого посредника auth на методе PHPcreate(). Так что это будет применяться только здесь и больше нигде. Почему бы нам это не попробовать? Итак, теперь articles должно работать, и да, работает. Но если мы пытаемся создать статью, мы прикрепили посредника, мы обнаружили что пользователь не вошёл в систему, так что мы его перенаправили.

(6:30)
Давайте зайдём как Джон и теперь на этот раз, поскольку мы аутентифицированы, у нас есть доступ к этой странице. Довольно легко. Совсем не трудно. И, как вы можете себе представить, в дополнение к only (только), вы также можете использовать except (кроме). Итак, я хочу, чтобы пользователь был аутентифицирован для каждой страницы, кроме страницы index, например:

PHP
$this->middleware('auth' , ['except' => 'index']);

Хорошо, теперь, страница index будет доступна, но ещё раз, любой другой маршрут, который подключен к тому контроллеру будет заставлять пользователя аутентифицироваться.

(7:00)
Теперь, если я переключусь обратно, ещё одна важная вещь — вы можете добавить его к конструктору вашего контроллера или вы можете добавить его индивидуально. Например, я мог бы сказать middleware и потом прикрепить его здесь, auth. И затем указать, что мы используем этот контроллер:

PHP
Route::get('about', ['middleware' => 'auth''uses' => 'PagesController@about']);

Так что если вы хотите присоединить этого посредника на уровне маршрута, так сказать, то вы могли бы это сделать так. Или, если не использовать контроллер, то вы могли бы сделать что-то вроде этого:

(7:30)

PHP
return 'this page will only show if the user is signed in';

ОК, давайте попробуем этот вариант чисто ради забавы. Перейдём к laravel5.dev/about, мы уже вошли, так что мы можем её видеть, но если мы выйдем из системы, а затем снова пойдём на страницу about, то она не будет для нас работать. Так что хорошо всё это знать, но я вернусь тут обратно к тому коду, что у нас здесь был раньше. Это я могу удалить. Теперь давайте вернемся к Kernel.php. Надеюсь, это начинает приобретать немного больше смысла.

(8:00)
Мы знаем, что если мы хотим чтобы посредник запускался для каждого отдельного запроса, то мы можем добавить его здесь. С другой стороны, если у нас просто есть специальный посредник, которого мы хотим прикрепить там где это целесообразно, то мы можем добавить его в этот массив (PHP$routeMiddleware). Мы даём ему ключ, ссылаемся на путь к посреднику и затем добавляем его туда, куда нам нужно. В этом случае, мы прикрепили его прямо здесь. Так что всё это хорошо. Мы начинаем всё это понимать. Но как насчет создания наших собственных? Хорошо, мы рассмотрим несколько примеров.

(8:30)
Давайте прокрутим файл вверх и рассмотрим пару из них. Вот здесь: PHPCheckForMaintenanceMode. Это то, что Laravel делает для нас стандартно по умолчанию. По сути, он позволяет нам сделать вот что. Если я запущу:

shphp artisan down

И обычно вы будете делать это на производстве. Что ж, это ставит наше приложение в режим обслуживания (maintenance), который по существу означает, что мы закрыли сайт, что даёт нам возможность выполнять любые обновления или сделать обновление Composer или нечто подобное. Так что теперь, если мы попытаемся посетить страницу home, то вы увидите данную надпись, «Мы скоро вернёмся» для каждого маршрута.

(9:00)
Мы находимся в режиме обслуживания. А затем, когда вы закончите с вашими обновлениями, вы можете запустить:

shphp artisan up

И теперь ваше приложение будет снова таким же, как вы можете здесь видеть. Так что это очень полезно. И как выясняется, это реализовано с использованием посредника. Давайте взглянем. PHPCheckForMaintenanceMode. И ещё раз, он выглядит точно так же, как и все другие посредники что мы видели, и это важно.

(9:30)
Он работает по типу декоратора, где мы отвечаем на запрос и выполняем некую логику, как правило, «охраняя» код за ним. И если там есть проблема, то мы можем PHPreject() или PHPredirect(), как мы делали в этом случае. Но если это не проблема, то мы работаем в этом разделе прямо здесь и на самом деле, тут всё очень легко читается. Переход к следующему запросу. И таким образом, следующий слой луковицы, так сказать, получит возможность делать то, что ему нужно делать. И ещё раз, он может отклонить запрос, или если всё проходит, он будет передавать запрос следующему слою луковицы.

(10:00)
Вот как это работает. Теперь, если вы хотите знать специфику этого всего, то всё что мы делаем, это проверяем находится ли приложение в режиме обслуживания. И в самом деле, всё что тут делается в таком случае, это создание файла. Поэтому, когда мы запускаем:

shphp artisan down

Laravel создаст небольшой файл вот здесь (storage/framework/down). И потом, когда мы делаем вызов этого метода, всё, что мы делаем здесь, это проверяем, существует ли этот файл, down.

(10:30)
Если он существует, значит пользователю нужно сообщить о закрытии, поэтому мы бросаем исключение, и когда мы его ловим, мы показываем этот небольшой вид. В противном случае, если приложение не в режиме техобслуживания, и давайте вернём его снова к работе:

shphp artisan up

мы выполним эту проверку. Файла down больше не существует, так что этот код не сработает. Наш посредник завершил работу, так что мы передаем запрос следующему слою. ОК, так как насчёт создания нашего собственного? Ну, как вы можете себе представить, как и для большинства вещей, у нас здесь есть генераторы.

(11:00)
Так что, если я запущу:

shphp artisan

Как и для всего остального, вы увидите у нас тут есть make:middleware. ОК:

shphp artisan make:middleware Demo

Давайте начнём с небольшой демонстрации. Хорошо, теперь вы видите, что у нас есть Demo.php и заготовка кода для начала. Теперь не забывайте, когда мы закончим здесь, мы хотим передать запрос на следующий слой луковицы. Вот так:

PHP
return $next($request);

сделает это за нас.

(11:30)
Так что бы мы могли сделать здесь просто для забавы? У нас есть доступ к запросу и, кстати, это будет экземпляром запроса PHPIlluminate\Http. Так что, если вы посмотрите на эти методы, у нас здесь есть множество классных вещей. Мы можем проверить, если URL соответствует какому-то шаблону. Так, например, вы могли бы сказать:

PHP
$request->is('articles/create')

“Это ли та страница с которой мы работаем”? Тогда проверяем здесь. Или мы могли бы сказать:

(12:00)
«Есть ли у запроса эта часть входных данных»? Или мы могли бы получить входные данные прямо там. Так что, мы могли бы сказать:

PHP
$request->input('name')

И это захватит значение из формы и от входных данных с именем name. Понятно? Так что здесь тонны всего интересного. Мы можем даже получить доступ к PHPuser(), который делает запрос или к аутентифицированному пользователю. Вот так:

PHP
$request->user()

А ещё пара примеров, мы могли бы получить маршрут, обрабатывающий запрос, или мы могли бы получить сегмент URI.

(12:30)
То есть конкретный кусок URI. Много всего классного здесь. Хорошо, давайте вернёмся к нашему посреднику. Почему бы нам просто для забавы, не сказать:

PHP
if ($request->has('foo'))

мы просто передадим его через строку запроса. Тогда это будет нашим триггером, и в этом случае мы захотим перенаправить его куда-нибудь через PHPredirect(). Так что мы скажем:

PHP
return redirect('articles');

Теперь, тут есть только одна вещь, ради которой нужно быть осторожным. Вы можете попасть в ловушку, где вы можете зациклиться.

(13:00)
Например, представьте, что вы добавили это для всех маршрутов. Таким образом, мы вставляем такой код в наш стек посредника вот здесь:

PHP
App\Http\Middleware\Demo

ОК, это значит что посредник будет работать для каждого запроса, что означает, что если мы посетим эту страницу, и у нас нет этого ключа foo, то произойдёт PHPredirect() на страницу articles. Но на этой странице также нет ключа и она будет делать PHPredirect() к себе снова и снова и снова.

(13:30)
И наше приложение упадёт. В таких ситуациях, и опять-таки, это всё просто для примера. Поскольку это не имеет никакого смысла на самом деле. Но мы могли бы сказать:

PHP
if ($request ->is('articles/create') && $request->has('foo'))

Только при этом условии, мы должны сделать PHPredirect(). Так что да, это будет работать. Давайте попробуем:

laravel5.dev/articles/create

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

(14:00)
Но теперь, мы сделаем так чтобы условие прошло успешно:

laravel5.dev/articles/create?foo=bar

Посредник запустился, условие прошло, поэтому мы отклонили запрос и перенаправились куда-то ещё, где сейчас та страница будет проходить через цикл посредника ещё раз. Надеюсь, это начинает приобретать смысл. И, конечно же, для таких вещей, как это, где это очень специфично, вы говорите: «Если URL – вот эта конкретная страница, и если у неё есть входные данные». Это было бы хорошо для форм запроса.

(14:30)
Или как минимум, вам не нужно, запускать это для запроса каждой страницы, если вы заинтересованы только в одной вот этой. В этом случае, если вы хотите использовать посредник, идём обратно к вашему Kernel.php, и переместим это из списка PHP$middleware, который работает для каждой страницы, и поместим его вот сюда. Теперь вы можете прикреплять его ключом там, где это уместно. Так что давайте сделаем ещё один пример вместе. Может быть, что-то немного более практичное. Представьте себе, что вы создаёте приложение, в котором вы хотите защитить определенные маршруты или определенные страницы,

(15:00)
так чтобы единственные люди, у которых был доступ к ним были нечто вроде менеджеров. Может быть, у вас есть пользователи, но ещё у вас есть менеджеры. Может быть, это было бы уместно для вашего приложения. Тогда если мы хотим сказать: «Эти наборы страниц могут быть доступны только для тех менеджеров», тогда мы можем создать нового посредника:

shphp artisan make:middleware

И просто в точности описать, что вы здесь делаете. Что делает этот посредник?

(15:30)
Что же, он должен:

RedirectIfNotAManager

ОК, давайте откроем его. В самом низу, я собираюсь перейти к следующему запросу, и вот наша проверка здесь. Теперь, как я уже сказал для $routeMiddleware, в точке запуска, вы можете сделать:

PHP
$request->user();

чтобы захватить доступ к аутентифицированному пользователю. Просто будьте осторожны. Если вы вместо этого, добавите его к списку выше, он может быть ещё не определён.

(16:00)
И в этом случае, вы захотели бы сделать что-то вроде этого:

PHP
$response = return $next($request);

Давайте запустим запрос, и вы можете:

PHP
return $response;

И потом вы можете сделать что-нибудь ещё после этого. Таким образом, в тот момент, даже для глобального посредника, PHP$request->user() будет установлен соответствующим образом. Что ж, давайте вернёмся к тому, что у нас было раньше. И давайте сделаем всё проще и скажем:

PHP
if ( ! $request->user()->isATeamManager())
{
    return 
redirect('articles');
}

(если авторизованный пользователь не является менеджером команды, то мы сделаем PHPredirect() куда угодно, articles подойдёт)

(16:30)
Хорошо, у нас есть наш посредник. Давайте пойдём дальше и создадим этот метод очень быстро на модели пользователя. Мы просто вставим тут:

PHP
public function isATeamManager()
{
    return 
false;
}

И теперь, если я переключусь обратно, не забывайте, что мы создали посредника, но мы также должны обновить наш файл Kernel.php:

PHP
'manager' => 'App\Http\Middleware\RedirectIfNotAManager'

Так почему бы нам не попробовать запустить это с фиктивным маршрутом? Мы скажем:

PHP
Route::get('foo', function()

И давайте здесь ссылаться на замыкание.

(17:00)
И теперь, я буду ссылаться на того посредника. Вот так:

PHP
Route::get('foo', ['middleware' => 'manager', function()

Хорошо, тогда:

PHP
return 'this page may only be viewed by managers';

Так что, если мы запустим это, мы не должны увидеть этот текст. Вернёмся. Давайте пойдём к laravel5.dev/foo и мы не менеджер, так что это сработало, и нас перенаправили на эту страницу. Хотя давайте изменим это. Давайте вернемся к пользователю, и изменим здесь на PHPtrue:

PHP
return true;

(17:30)
И теперь, если мы попробуем ещё раз, мы можем видеть текст. Хорошо, таким образом, надеюсь, если изначально это понятие посредников было немного запутанным и сложным, то, надеюсь, теперь, когда вы прошли через это видео, вы увидели, что на самом деле это не самая сложная вещь в мире.

Как вы считаете, полезен ли этот материал? Да Нет

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.