jwt laravel_如何构建仅由JWT驱动的API的Laravel应用

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的客户端应用程序。

Laravel Logo

创建项目 (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密钥都将自动生成。

Everything went well

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.

在执行任何操作之前,我们必须计划应用程序的详细信息和功能。 我们将构建一个基本的图书愿望清单,因此我们将使用两个主要实体: UserBook

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文件,我们可以看到它已经具有signuplogin方法! 它们不是我们在标准的新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 。 让我们填写我们的请求正文...

Request body filled out

… send it and wait for the result …

……发送并等待结果……

Result produced

Done! Our user is now added to the application. To be sure, let’s make another test. This time for the login.

做完了! 现在,我们的用户已添加到应用程序中。 可以肯定的是,让我们进行另一个测试。 这次进行login

Logging in via postman

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).

我们将这些字段( titleauthor_namepages_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的示例。

Token filled out for storing

… and then …

… 然后 …

Index lists out books

It works!

有用!

We can finish the job by implementing the three remaining methods: show, update and destroy.

我们可以通过实现其余三种方法来完成工作: showupdatedestroy

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.

showupdatedestroy ,我们使用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.

注意:我们可能会在使用某些身体数据使用PUTDELETE动词时遇到一些问题。 如果正文始终为空 ,请尝试使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值