Статьи

Как создать приложение Laravel на платформе JWT только для API

Котельная табличка Laravel API (JWT Edition) была сделана на плечах гигантов, таких как:

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

Laravel Logo

Создание проекта

В этом руководстве предполагается, что настроена рабочая среда PHP, что-то вроде Homestead Improved . Давайте установим наш API Boilerplate. Все, что нам нужно сделать, это клонировать репозиторий Github , с

git clone https : / / github . com / francescomalatesta / laravel - api - boilerplate - jwt Laravel 

тогда беги

 composer install 

установить зависимости и выполнить все основные операции по настройке. Ключи Laravel и JWT будут сгенерированы автоматически.

Все прошло гладко

Больше нечего делать! Давайте создадим серверную часть нашего приложения!

Создание API

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

Пользователь сможет:

  • зарегистрируйтесь, чтобы создать новый аккаунт;
  • войти, если у них уже есть аккаунт;
  • Выйти, чтобы оставить заявку;
  • получить список своих книг;
  • добавить новую книгу в список желаний;
  • редактировать существующую книгу;
  • удалить существующую книгу из списка;

Обратите внимание, что для каждого пользователя мы будем просить:

  • имя;
  • Адрес электронной почты;
  • пароль;

Книга будет иметь:

  • заглавие;
  • имя автора;
  • количество страниц;

Наконец, в каждом списке будут отображаться книги, начиная с самой последней.

Первый шаг: пользовательская часть.

Сборка пользовательской части

Хорошей новостью является то, что пользовательская часть уже завершена.

Если мы app/Api/V1/Controllers/AuthController.php файл app/Api/V1/Controllers/AuthController.php , мы увидим, что он уже имеет signup и метод login ! Это не те, которые мы можем видеть в стандартном AuthController в стандартном новом проекте Laravel: здесь они были повторно реализованы, чтобы иметь процедуру регистрации и входа в систему на основе токенов.

Теперь давайте посмотрим на метод signup , в частности.

 public function signup ( Request $request ) { $signupFields = Config : : get ( 'boilerplate.signup_fields' ) ; $hasToReleaseToken = Config : : get ( 'boilerplate.signup_token_release' ) ; $userData = $request - > only ( $signupFields ) ; $validator = Validator : : make ( $userData , Config : : get ( 'boilerplate.signup_fields_rules' ) ) ; if ( $validator - > fails ( ) ) { throw new ValidationHttpException ( $validator - > errors ( ) - > all ( ) ) ; } User : : unguard ( ) ; $user = User : : create ( $userData ) ; User : : reguard ( ) ; if ( ! $user - > id ) { return $this - > response - > error ( 'could_not_create_user' , 500 ) ; } if ( $hasToReleaseToken ) { return $this - > login ( $request ) ; } return $this - > response - > created ( ) ; } 

Как видно из кода, мы используем некоторые параметры в файле конфигурации. В частности, мы получаем два значения из этого файла:

  • boilerplate.signup_fields : список полей, которые мы хотим использовать для операции регистрации;
  • boilerplate.signup_fields_rules : правила проверки, с которыми мы хотим проверять наши данные во время операции регистрации;

Мы можем найти все параметры шаблона в config/boilerplate.php .

Здесь также используется другая опция : boilerplate.signup_token_release . Если установлено значение true , токен автоматически освобождается после регистрации. Это означает, что пользователь может сразу начать использовать ваше приложение .

Примечание: для этого проекта мы будем использовать 24-часовые токены. Значение по умолчанию для срока службы токена составляет 60 минут. Это можно изменить в config/jwt.php со значением ttl .

Вернуться к нашему API. Фактическая настройка для boilerplate.signup_fields :

 'signup_fields' = > [ 'name' , 'email' , 'password' ] , 

… и это нормально, учитывая наши потребности. Также есть несколько основных правил:

 'signup_fields_rules' = > [ 'name' = > 'required' , 'email' = > 'required|email|unique:users' , 'password' = > 'required|min:6' ] , 

Пока нам ничего не нужно было делать. Также у нас уже есть наши маршруты в файле app/Http/api_routes.php .

 // app/Http/api_routes.php $api = app ( 'Dingo\Api\Routing\Router' ) ; $api - > version ( 'v1' , function ( $api ) { $api - > post ( 'auth/login' , 'App\Api\V1\Controllers\AuthController@login' ) ; $api - > post ( 'auth/signup' , 'App\Api\V1\Controllers\AuthController@signup' ) ; . . . } ) ; 

Маршрутизатор, который мы здесь используем, немного отличается от обычного, который мы видим в Laravel. Фактически мы используем пользовательский Dingo API Router.

Пользовательская часть теперь завершена без ввода единой строки кода.

Единственная оставшаяся часть — это процедура logout из logout . Однако RESTful API по определению не имеет состояния, и это то, что мы собираемся реализовать на клиенте.

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

  php artisan migrate 

Давайте проверим базовую процедуру регистрации. Мы откроем HTTP-клиент и позвоним по маршруту signup . Для этой статьи мы будем использовать Почтальон . Давайте заполним наше тело запроса …

Тело запроса заполнено

… отправить его и дождаться результата …

Результат произведен

Выполнено! Наш пользователь теперь добавлен в приложение. Чтобы быть уверенным, давайте сделаем еще один тест. На этот раз для login в login .

Вход через почтальона

Большой! Процедура login также работает нормально! Нам больше ничего не нужно для пользовательской части. Пришло время закончить наш API путем реализации части книги .

Создание части книги

Чтобы реализовать сторону Book, нам потребуется миграция, чтобы определить структуру нашей схемы, модель и, наконец, контроллер для обработки всех операций. Начнем с миграций . Давайте откроем терминал и наберем

  php artisan make : migration create_books_table -- create = books 

и новый файл будет создан в database/migrations . Затем мы добавляем следующие поля в метод up() .

 public function up ( ) { Schema : : create ( 'books' , function ( Blueprint $table ) { $table - > increments ( 'id' ) ;  // adding specific fields here... $table - > string ( 'title' ) ; $table - > string ( 'author_name' ) ; $table - > integer ( 'pages_count' ) ; $table - > integer ( 'user_id' ) - > index ( ) ; $table - > timestamps ( ) ; } ) ; } 

Время снова «мигрировать», набрав

  php artisan migrate 

добавить новую таблицу в схему. Теперь давайте создадим модель, с

  php artisan make : model Book 

и мы сделали. Из-за соглашений в Laravel модель Book уже «знает», что она должна работать с созданной нами таблицей books . Вот модель:

 namespace App ; use Illuminate \ Database \ Eloquent \ Model ; class Book extends Model { protected $fillable = [ 'title' , 'author_name' , 'pages_count' ] ; } 

Мы собираемся поместить эти поля ( title , author_name и pages_count ) в массив для fill чтобы использовать метод fill в процедуре обновления (подробнее об этом в ближайшее время).

Теперь нам нужно отредактировать нашу модель User , чтобы определить отношения, которые нам понадобятся для получения связанных книг. В app\User.php мы добавляем следующий метод:

 public function books ( ) { return $this - > hasMany ( 'App\Book' ) ; } 

Последний шаг — это контроллер ресурсов для сущности Book . Давайте набирать

  php artisan make : controller BookController 

создать новый контроллер ресурса. Laravel создаст его в папке app/Http/Controllers . Мы переместим его в app/Api/V1/Controllers .

Мы также изменим пространство имен:

 namespace App\Api\V1\Controllers; 

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

Прежде чем продолжить, мы должны не забыть добавить некоторые директивы use в начале файла:

 use JWTAuth ; use App \ Book ; use Dingo \ Api \ Routing \ Helpers ; 

Первый — это JWTAuth Facade, который мы будем использовать для извлечения наших пользовательских данных из токена. Второй — наша модель. Третий — помощник, который мы будем использовать для быстрого создания каждого ответа для вывода клиенту в режиме RESTful.

Мы также должны помнить о включении этой черты Helpers в контроллер:

 . . . class BookController extends Controller { use Helpers ; . . . 

Теперь мы можем реализовать наши методы:

  • index , чтобы получить список книг определенного пользователя;
  • show , чтобы получить одну книгу с указанным номером;
  • store , чтобы сохранить вновь созданную книгу в базе данных;
  • update , чтобы хранить обновления для определенной книги, учитывая ее идентификатор;
  • destroy , чтобы удалить существующую книгу из базы данных, учитывая ее идентификатор;

Начнем с первого: index() .

 public function index ( ) { $currentUser = JWTAuth : : parseToken ( ) - > authenticate ( ) ; return $currentUser - > books ( ) - > orderBy ( 'created_at' , 'DESC' ) - > get ( ) - > toArray ( ) ; } 

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

Мы можем сделать шаг вперед, чтобы store()

 public function store ( Request $request ) { $currentUser = JWTAuth : : parseToken ( ) - > authenticate ( ) ; $book = new Book ; $book - > title = $request - > get ( 'title' ) ; $book - > author_name = $request - > get ( 'author_name' ) ; $book - > pages_count = $request - > get ( 'pages_count' ) ; if ( $currentUser - > books ( ) - > save ( $book ) ) return $this - > response - > created ( ) ; else return $this - > response - > error ( 'could_not_create_book' , 500 ) ; } 

Как и раньше, мы получаем текущего пользователя с JWTAuth метода parseToken()->authenticate() , parseToken()->authenticate() . Затем мы создаем наш экземпляр и сохраняем связь с помощью метода save .

Если все пойдет правильно, ответ Created 201 будет возвращен. В противном случае клиент получит пользовательскую ошибку 500 . Как мы видим, речь идет не только о теле: заголовки также будут соответствующим образом настроены для соответствия стандартам RESTful.

Давайте сделаем несколько тестов, прежде чем закончить наш контроллер. Добавьте следующий код в api_routes.php :

 $api - > group ( [ 'middleware' = > 'api.auth' ] , function ( $api ) { $api - > post ( 'book/store' , 'App\Api\V1\Controllers\BookController@store' ) ; $api - > get ( 'book' , 'App\Api\V1\Controllers\BookController@index' ) ; } ) ; 

У нас есть оба маршрута сейчас! Вернемся к нашему HTTP-клиенту и снова войдем в систему. С нашим новым токеном давайте сделаем несколько запросов на сохранение новой книги и сразу же получим их список.

Мы можем передать токен двумя способами: передать его в виде простого параметра запроса или в заголовке с этим конкретным элементом: Authorization: Bearer {token_goes_here} .

Тем не менее, вот пример запроса store и последующего index .

Токен заполнен для хранения

… а потом …

Индекс перечисляет книги

Оно работает!

Мы можем закончить работу, реализовав три оставшихся метода: show , update и destroy .

Вот они!

 . . . public function show ( $id ) { $currentUser = JWTAuth : : parseToken ( ) - > authenticate ( ) ; $book = $currentUser - > books ( ) - > find ( $id ) ; if ( ! $book ) throw new NotFoundHttpException ; return $book ; } public function update ( Request $request , $id ) { $currentUser = JWTAuth : : parseToken ( ) - > authenticate ( ) ; $book = $currentUser - > books ( ) - > find ( $id ) ; if ( ! $book ) throw new NotFoundHttpException ; $book - > fill ( $request - > all ( ) ) ; if ( $book - > save ( ) ) return $this - > response - > noContent ( ) ; else return $this - > response - > error ( 'could_not_update_book' , 500 ) ; } public function destroy ( $id ) { $currentUser = JWTAuth : : parseToken ( ) - > authenticate ( ) ; $book = $currentUser - > books ( ) - > find ( $id ) ; if ( ! $book ) throw new NotFoundHttpException ; if ( $book - > delete ( ) ) return $this - > response - > noContent ( ) ; else return $this - > response - > error ( 'could_not_delete_book' , 500 ) ; } . . . 

Мы продолжали использовать $currentUser как неявную «проверку безопасности». В реальных случаях вы должны создать что-то более надежное.

Также возможно выдать некоторые исключения Symfony. API автоматически распознает их и закодирует в правильном формате вывода. Вы можете найти полный список поддерживаемых исключений здесь .

При show , update и destroy мы использовали NotFoundHttpException чтобы сообщить клиенту, что мы не нашли определенный ресурс.

Теперь все, что нам нужно сделать, это добавить последние три новых маршрута в файл api_routes.php .

 $api - > group ( [ 'middleware' = > 'api.auth' ] , function ( $api ) { $api - > get ( 'books' , 'App\Api\V1\Controllers\BookController@index' ) ; $api - > get ( 'books/{id}' , 'App\Api\V1\Controllers\BookController@show' ) ; $api - > post ( 'books' , 'App\Api\V1\Controllers\BookController@store' ) ; $api - > put ( 'books/{id}' , 'App\Api\V1\Controllers\BookController@update' ) ; $api - > delete ( 'books/{id}' , 'App\Api\V1\Controllers\BookController@destroy' ) ; } ) ; 

Laravel (и Dingo) имеет метод ярлыка resource() который мы можем использовать, чтобы связать все маршруты, которые мы только что видели, с помощью одной инструкции.

 $api - > resource ( 'books' , 'App\Api\V1\Controllers\BookController' ) ; 

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

Контроллер завершен. Вот оно в полном объеме !

Примечание: мы могли бы наткнуться на некоторые проблемы, используя глаголы PUT и DELETE с некоторыми данными тела. Если тело всегда пустое , попробуйте использовать x-www-form-urlencoded вместо form-data . Это должно решить проблему.

Серверная часть готова!

Вывод

В этой части мы сосредоточились на серверной части и использовали довольно стабильный и простой в использовании шаблон для построения нашего API и процесса аутентификации + регистрации. Так вот, наше приложение уже дружественное к клиенту. Соедините приложение для iOS или Android или даже одну из множества платформ JavaScript, и вы с легкостью сможете использовать содержимое нашего приложения. Именно на это мы и обратим внимание во второй части — создании приложения Angular для этого бэкэнда с нуля! Будьте на связи!