jwt laravel
The Laravel API Boilerplate (JWT Edition) was made on the shoulders of giants like:
Laravel API Boilerplate(JWT版)是在巨人的肩膀上制成的,例如:
In this article, we will learn how to use it to quickly create a fully functional API for an imaginary book wishlist application. As an aside, we will also see how to build a client application with AngularJS that will use our APIs.
在本文中,我们将学习如何使用它为虚构的图书愿望清单应用快速创建功能齐全的API。 顺便说一句,我们还将看到如何使用AngularJS构建将使用我们的API的客户端应用程序。
创建项目 (Creating the Project)
This tutorial assumes a working PHP environment has been set up, something like Homestead Improved. Let’s install our API Boilerplate. All we have to do is to clone the Github repository, with
本教程假定已经建立了一个可工作PHP环境,例如Homestead Improvement 。 让我们安装我们的API样板。 我们要做的就是克隆Github存储库 ,
git clone https://github.com/francescomalatesta/laravel-api-boilerplate-jwt Laravel
then run
然后跑
composer install
to install dependencies and make all the basic configuration operations. Both Laravel and JWT keys will be automatically generated.
安装依赖项并进行所有基本配置操作。 Laravel和JWT密钥都将自动生成。
Nothing more to do! Let’s build the server part of our app!
没事做! 让我们构建应用程序的服务器部分!
创建API (Creating the API)
Before doing anything, we have to plan our application’s details and features. We are going to build a basic book wishlist, so we will be using two main entities: User and Book.
在执行任何操作之前,我们必须计划应用程序的详细信息和功能。 我们将构建一个基本的图书愿望清单,因此我们将使用两个主要实体: User和Book 。
A User will be able to:
用户将能够:
- sign up, in order to create a new account; 注册,以创建一个新帐户;
- log in, if they already have an account; 登录,如果他们已经有一个帐户;
- log out, to leave the application; 注销,离开应用程序;
- get a list of their books; 得到他们的书单;
- add a new book to the wishlist; 向心愿单添加一本新书;
- edit an existing book; 编辑现有书籍;
- delete an existing book from the list; 从列表中删除现有书籍;
Note that for every User we will ask for:
请注意,对于每个用户,我们将要求:
- name; 名称;
- email address; 电子邮件地址;
- password; 密码;
A Book will have a:
一本书将具有:
- title; 标题;
- author name; 作者姓名;
- pages count; 页数;
Finally, every list will show books starting from the most recent one.
最后,每个列表都将显示最新书籍。
First step: the User part.
第一步: 用户部分。
构建用户部分 (Building the User Part)
The good news is that the User part is already completed.
好消息是用户部分已经完成。
If we open the app/Api/V1/Controllers/AuthController.php
file, we can see that it already has a signup
and a login
method! They are not the the ones we can see in the standard AuthController
in a standard new Laravel project: here they were re-implemented to have a token-based sign up and log in procedure.
如果打开app/Api/V1/Controllers/AuthController.php
文件,我们可以看到它已经具有signup
和login
方法! 它们不是我们在标准的新Laravel项目中的标准AuthController
中看到的AuthController
:在这里,它们被重新实现以具有基于令牌的注册和登录过程。
Now, let’s take a look at the signup
method, in particular.
现在,让我们来看一下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();
}
As we can see from the code, we are using some options in a config file. Specifically, we are getting two values from this file:
从代码中可以看到,我们在配置文件中使用了一些选项。 具体来说,我们从该文件中获得两个值:
boilerplate.signup_fields
: the fields list we want to use for the signup operation;boilerplate.signup_fields
:我们要用于注册操作的字段列表;boilerplate.signup_fields_rules
: the validation rules we want our data checked against during the signup operation;boilerplate.signup_fields_rules
:在注册操作期间我们要检查数据的验证规则;
We can find all the boilerplate options in config/boilerplate.php
.
我们可以在config/boilerplate.php
找到所有样板选项。
There’s also another option used here: boilerplate.signup_token_release
. When set to true
, a token is automatically released after signup. This means that the user can start using your app immediately.
这里还使用了另一个选项: boilerplate.signup_token_release
。 设置为true
,注册后会自动释放令牌。 这意味着用户可以立即开始使用您的应用程序 。
Note: for this project we are going to use 24-hour tokens. The default value for the token’s life-span is 60 minutes. This can be changed in config/jwt.php
at the ttl
value.
注意:对于本项目,我们将使用24小时令牌。 令牌寿命的默认值为60分钟。 可以在config/jwt.php
中的ttl
值处进行更改。
Back to our API. The actual setting for boilerplate.signup_fields
is
回到我们的API。 boilerplate.signup_fields
的实际设置为
'signup_fields' => [
'name', 'email', 'password'
],
… and it’s fine, considering our needs. Some basic rules are there, too:
……考虑到我们的需求也很好。 也有一些基本规则:
'signup_fields_rules' => [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|min:6'
],
So far, we haven’t had to do anything. Also, we already have our routes in the app/Http/api_routes.php
file.
到目前为止,我们无需做任何事情。 另外,我们的路线已经在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');
...
});
The router we’re using here is a little bit different from the normal one we see in Laravel. We are, in fact, using the custom Dingo API Router.
我们在这里使用的路由器与我们在Laravel中看到的普通路由器有些不同。 实际上,我们正在使用自定义的Dingo API路由器。
The user part is now complete without typing a single line of code.
用户部分现在完成,无需键入任何代码。
The only one remaining part is the logout
procedure. However, a RESTful API is stateless by definition and this is something we are going to implement in the client.
剩下的唯一部分是logout
过程。 但是,RESTful API根据定义是无状态的,这是我们将在客户端中实现的东西。
Now, we should at least test our two procedures, to be sure. The first thing to do is to migrate our database. In our terminal, let’s type
现在, 我们至少应该测试两个过程 。 首先要做的是迁移我们的数据库 。 在我们的终端中,输入
php artisan migrate
Let’s test a basic signup procedure. We’ll open an HTTP Client and make a call to the signup
route. For this article’s purposes, we will use Postman. Let’s fill our request body…
让我们测试一个基本的注册过程。 我们将打开一个HTTP客户端,并调用signup
路由。 出于本文的目的,我们将使用Postman 。 让我们填写我们的请求正文...
… send it and wait for the result …
……发送并等待结果……
Done! Our user is now added to the application. To be sure, let’s make another test. This time for the login
.
做完了! 现在,我们的用户已添加到应用程序中。 可以肯定的是,让我们进行另一个测试。 这次进行login
。
Great! The login
procedure also works fine! We don’t need anything else for the User part. Time to finish our API by implementing the Book part.
大! login
过程也可以正常工作! 用户部分不需要其他任何东西。 是时候通过实现Book部分来完成我们的API。
构建书本部分 (Building the Book Part)
In order to implement the Book side, we will need a migration to define our schema structure, a model and, finally, a controller to handle all the operations. Let’s start with migrations. Let’s open the terminal and type
为了实现Book方面,我们将需要进行迁移以定义我们的架构结构,一个模型,最后是一个控制器来处理所有操作。 让我们从迁移开始。 打开终端并输入
php artisan make:migration create_books_table --create=books
and a new file will be created in database/migrations
. We then add the following fields in the up()
method.
并且将在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();
});
}
Time to “migrate” again, by typing
是时候通过键入再次“迁移”
php artisan migrate
to add the new table to the schema. Now, let’s create the model, with
将新表添加到架构。 现在,让我们创建模型,
php artisan make:model Book
and we are done. Due to the conventions in Laravel, the Book
model already “knows” that it has to work with the books
table we have created. Here’s the model:
我们完成了。 由于Laravel中的约定, Book
模型已经“知道”它必须与我们创建的books
表一起使用。 这是模型:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
protected $fillable = ['title', 'author_name', 'pages_count'];
}
We are going to put these fields (title
, author_name
and pages_count
) into the fillable
array in order to use the fill
method on the update procedure (more on that soon).
我们将这些字段( title
, author_name
和pages_count
)放入fillable
数组中,以便在更新过程中使用fill
方法(稍后将进行介绍)。
Now, we have to edit our User
model in order to define the relationship we will need to retrieve their related books. In app\User.php
we add the following method:
现在,我们必须编辑我们的User
模型,以便定义检索其相关书籍所需的关系。 在app\User.php
我们添加以下方法:
public function books()
{
return $this->hasMany('App\Book');
}
The last step is the resource controller for the Book
entity. Let’s type
最后一步是Book
实体的资源控制器。 让我们输入
php artisan make:controller BookController
to create a new resource controller. Laravel will create it in the app/Http/Controllers
folder. We will move it to app/Api/V1/Controllers
.
创建一个新的资源控制器。 Laravel将在app/Http/Controllers
文件夹中创建它。 我们将其移至app/Api/V1/Controllers
。
We also change the namespace:
我们还更改了名称空间:
namespace App\Api\V1\Controllers;
where App
is the basic namespace we have chosen for our application. By default, we can leave it as it is.
App
是我们为应用程序选择的基本名称空间。 默认情况下,我们可以保持原样。
Before proceeding, we have to remember to add some use
directives at the beginning of the file:
在继续之前,我们必须记住在文件的开头添加一些use
指令:
use JWTAuth;
use App\Book;
use Dingo\Api\Routing\Helpers;
The first one is the JWTAuth Facade, which we will use to retrieve our user data from the token. The second one is our model. The third is a helper we will use to quickly create every response for outputting to the client in a RESTful manner.
第一个是JWTAuth Facade,我们将使用它从令牌中检索用户数据。 第二个是我们的模型。 第三个是帮助器,我们将使用它来快速创建每个响应,以RESTful方式输出到客户端。
We must also remember to include this Helpers
trait in the controller:
我们还必须记住在控制器中包含此Helpers
特性:
...
class BookController extends Controller
{
use Helpers;
...
Now we can implement our methods:
现在我们可以实现我们的方法:
index
, to get the books list of a certain user;index
,获取特定用户的图书清单;show
, to get a single book given its id;show
,得到一本书的ID;store
, to store a newly created book in the database;store
,将新创建的书存储在数据库中;update
, to store updates on a certain book, given its id;update
,用于在给定ID的情况下将更新存储在特定书籍中;destroy
, to delete an existing book from the database, given its id;destroy
,从给定ID的数据库中删除现有书籍;
Let’s start from the first: index()
.
让我们从第一个开始: index()
。
public function index()
{
$currentUser = JWTAuth::parseToken()->authenticate();
return $currentUser
->books()
->orderBy('created_at', 'DESC')
->get()
->toArray();
}
This is really easy to understand. We are getting our user starting from the token, and then we are returning all the related books as output. Nothing more.
这真的很容易理解。 我们使用户从令牌开始,然后返回所有相关书籍作为输出。 而已。
We can make a step forward, to store()
我们可以向前迈一步,到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);
}
Like before, we are getting the current user with the JWTAuth
method, parseToken()->authenticate()
. Then, we are creating our instance and saving the relationship with the save
method.
像以前一样,我们使用JWTAuth
方法parseToken()->authenticate()
获取当前用户。 然后,我们将创建实例并使用save
方法save
关系。
If everything goes right, a Created 201
response will be returned. Otherwise, the client will get a custom 500
error. As we can see, it’s not just about the body: headers also will be tweaked accordingly to follow RESTful standards.
如果一切正常,将返回Created 201
响应。 否则,客户端将收到自定义500
错误。 正如我们所看到的,它不仅是正文:标头也将相应地进行调整以遵循RESTful标准。
Let’s do some tests before finishing our controller. Add the following code into api_routes.php
:
让我们在完成控制器之前做一些测试。 将以下代码添加到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');
});
We have both routes now! Let’s get back to our HTTP Client and log in again. With our new token, let’s make some requests to store a new book and, right after, get a list of them.
我们现在都有两条路线! 让我们回到我们的HTTP客户端并再次登录。 使用我们的新令牌,让我们提出一些存储新书的请求,然后立即获取它们的列表。
We can pass the token in two ways: passing it as a simple request parameter, or in the header with this specific item: Authorization: Bearer {token_goes_here}
.
我们可以通过两种方式传递令牌:将其作为简单的请求参数传递,或在具有以下特定项目的标头中传递: Authorization: Bearer {token_goes_here}
。
That said, here’s an example of a store
request and the subsequent index
one.
就是说,这是一个store
请求和后续index
1的示例。
… and then …
… 然后 …
It works!
有用!
We can finish the job by implementing the three remaining methods: show
, update
and destroy
.
我们可以通过实现其余三种方法来完成工作: show
, update
和destroy
。
Here they are!
他们来了!
...
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);
}
...
We continued to use the $currentUser
as an implicit “safety check”. In real world cases you should build something more robust.
我们继续将$currentUser
用作隐式“安全检查”。 在实际情况下,您应该构建更强大的功能。
It’s also possible to throw some Symfony exceptions. The API will automatically recognize them and encode in the right output format. You can find the complete supported exceptions list here.
也有可能引发一些Symfony异常。 API将自动识别它们并以正确的输出格式进行编码。 您可以在此处找到完整的受支持例外列表。
In show
, update
and destroy
, we used the NotFoundHttpException
to tell the client that we have not found a certain resource.
在show
, update
和destroy
,我们使用NotFoundHttpException
来告知客户端我们尚未找到某个资源。
Now, all we have to do is to add the last three new routes to the api_routes.php
file.
现在,我们要做的就是将最后三个新路由添加到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 (and Dingo) has the shortcut method resource()
that we can use to bind all the routes we just saw with a single instruction.
Laravel(和Dingo)具有快捷方法resource()
,我们可以使用它通过一条指令来绑定我们刚刚看到的所有路由。
$api->resource('books', 'App\Api\V1\Controllers\BookController');
Choose the approach you like the most.
选择最喜欢的方法。
The controller is now complete. Here it is in full!
控制器现已完成。 这里已经满了 !
Note: we could stumble upon some issues using PUT
and DELETE
verbs with some body data. If the body is always empty, try to use x-www-form-urlencoded
instead of form-data
. It should fix the problem.
注意:我们可能会在使用某些身体数据使用PUT
和DELETE
动词时遇到一些问题。 如果正文始终为空 ,请尝试使用x-www-form-urlencoded
代替form-data
。 它应该解决问题。
The server part is done!
服务器部分已完成!
结论 (Conclusion)
In this part, we focused on the back-end and used a fairly stable and easy to use boilerplate to build our API and authentication + registration flow. Just like that, our app is already client-friendly. Slap together an iOS or Android app or even one of the many JavaScript frameworks and you can consume the content of our application with ease. That’s exactly what we’ll be focusing on in part 2 – building an Angular app for this back end from scratch! Stay tuned!
在这一部分中,我们专注于后端,并使用了相当稳定且易于使用的样板来构建我们的API和身份验证+注册流程。 就像这样,我们的应用程序已经对客户端友好。 将iOS或Android应用程序甚至许多JavaScript框架之一放在一起,您就可以轻松使用我们应用程序的内容。 这正是我们将在第2部分中重点介绍的-从头开始为该后端构建Angular应用! 敬请关注!
翻译自: https://www.sitepoint.com/how-to-build-an-api-only-jwt-powered-laravel-app/
jwt laravel