Котельная табличка Laravel API (JWT Edition) была сделана на плечах гигантов, таких как:
В этой статье мы узнаем, как использовать его для быстрого создания полнофункционального API для приложения с воображаемым списком пожеланий. Кроме того, мы также увидим, как создать клиентское приложение с AngularJS, которое будет использовать наши API.
Создание проекта
В этом руководстве предполагается, что настроена рабочая среда 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 для этого бэкэнда с нуля! Будьте на связи!