用 Laravel 构建 RESTful API 详细教程
不管是你平时用的各种 APP - 滴滴美团,到各种银行网站,我们的现代世界其实是由许多 API 所驱动的。在本文中,你会学到如何用 Laravel 构建一个功能齐备的 RESTful API,并且通过一个应用来实现这些 API。
如果你正在用 Laravel 构建后端 API,同时需要搜索功能的话,欢迎尝试卡拉搜索 API。几行代码就可以接入搜索功能,后台统一管理免掉开发和运维烦恼
环境准备
- PHP 7.1 或者更高版本
- Composer
- MySQL
- Laravel 5.6 或更高版本
- Postman
请注意,在本文中我们假设你有一些对于 PHP 以及 Laravel 框架的基本理解。
理解我们的应用到底要干啥
咱们要构建一组 CRUD API。CRUD的含义是 Create,Read,Update 和 Delete,也就是增删改查。
这个例子中,我们会假设在给一个学校做一个学生查询系统。我们的 API 会有这些接口:
GET /api/students
接受 GET
请求,并返回所有的学生。
GET /api/students/{id}
接受 GET
请求,通过传入学生的 id
来返回一个学生的数据。
POST /api/students
接受 POST
请求,创建一个学生。
DELETE /api/students/{id}
接受 DELETE
请求,会通过一个学生的 id
删除一个学生。
每条学生数据仅包含 name
和 course
作为详情。当你开发好这些接口以后,你要使用这些接口来完成一个学生信息管理应用。
设置好 Laravel 应用
首先,创建一个 Laravel 应用。你可以在终端执行这行命令:
$ laravel new api-project
然后进入你刚创建的项目:
$ cd api-project
启动 Laravel 服务器:
$ php artisan serve
然后你就可以通过 https://localhost:8000 访问到你的应用了
现在我们创建一个数据库,在终端输入:
$ mysql -uroot -p
如果你还没登陆过的话,你会被要求输入你的MySQL密码。接着,通过这个命令我们来创建一个叫 api-project
的新数据库:
CREATE DATABASE `api-project`;
我们继续创建一个 Laravel 模型 以及对应的数据迁移:
$ php artisan make:model Student -m
在你的 app
文件夹里就会有一个叫 Student.php
的文件
注意:你必须在文件里手写清楚我们到底要操作哪张表的哪些字段:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $table = 'students';
protected $fillable = ['name', 'course'];
}
此外,在 database/migrations
文件夹里会生成一个数据迁移文件。你需要修改这个数据迁移文件,给它创建两列接受字符串的字段 name
和 course
。
...
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('course');
$table->timestamps();
});
}
...
然后把项目用你熟悉的文本编辑打开,修改 .env
文件,在里面填好你的数据库相关信息,填好之后你的应用才可以连接数据库:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<your-database-name>
DB_USERNAME=<your-database-username>
DB_PASSWORD=<your-database-password>
最后,运行你的数据迁移:
$ php artisan migrate
设置路由
现在应用的基本工作我们做好了,接下来我们要搞一个 controller
来写我们的 API 方法们。运行这行命令:
$ php artisan make:controller ApiController
在 app/http/controllers
文件夹下会创建一个新文件 ApiController.php
,我们在里面加上这些方法:
...
class ApiController extends Controller
{
public function getAllStudents() {
// logic to get all students goes here
}
public function createStudent(Request $request) {
// logic to create a student record goes here
}
public function getStudent($id) {
// logic to get a student record goes here
}
public function updateStudent(Request $request, $id) {
// logic to update a student record goes here
}
public function deleteStudent ($id) {
// logic to delete a student record goes here
}
}
再来到 routes
文件夹,并打开 api.php
这个文件,在里面引用我们之前在 ApiController
里写好的方法:
...
Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}','ApiController@deleteStudent');
注意:api.php
里所有的路由默认都是以 /api
开头的。
创建一条学生数据
我们来到 ApiController
中的 createStudent
方法
public function createStudent(Request $request) {
// logic to create a student record goes here
}
我们会使用 Laravel request 类来获取传进来的参数。接口会要求你传进来一个 string
类型的 name
和 一个 string
类型的 course
。 我们成功的拿到数据后,就要把数据存到数据库里去。
...
use App\Student;
class ApiController extends Controller
{
...
public function createStudent(Request $request) {
$student = new Student;
$student->name = $request->name;
$student->course = $request->course;
$student->save();
return response()->json([
"message" => "student record created"
], 201);
}
...
}
在上面的代码中,引入了负责操纵 students
表的 Student
模型。在 createStudent
方法中,我们在方法参数里初始化了一个新的 Request
对象,它里面有一个 Student
对象。最后,所有的 $student-><字段名>
所代表的字段都被存了下来。
如果操作成功的话,API 会返回一个 JSON 以及一个提示消息 student record created
并且返回状态码 201
。
这个方法已经和 api/students
绑定好了,因为我们之前在路由文件 routes/api.php
里写了:
...
Route::post('students, 'ApiController@createStudent');
...
测试
在测试之前,检查你的应用有没有在运行。你可以用这个之前讲过一遍的命令来运行:
$ php artisan serve
或者你可以用 Valet 这个工具来给你所有的 PHP 应用创建一个代理转发,它会给你一个 *.test
或者 *.dev
的域名来方便你进行应用测试。
要测试我们刚写好的 API, 你需要打开 Postman 来发一个请求到 http://localhost:8000/api/students
。如果你用的是 Valet ,那你就要发到 http://<folder-name>/api/students
。选择 form-data
这个选项,并把值如图所示地传进去:
如果成功的话,它会返回成功消息以及一个 201
的状态码。现在你再多创建点学生数据进去吧,下一个环节会用到。
返回所有的学生数据
现在让我们来到 ApiController
的 getAllStudents
方法
public function getAllStudents() {
// logic to get all students goes here
}
我们使用已经引入进来的 Student
模型来发出一个简单的 eloquent 查询来返回数据库里所有的学生
class ApiController extends Controller
{
public function getAllStudents() {
$students = Student::get()->toJson(JSON_PRETTY_PRINT);
return response($students, 200);
}
...
}
这条 eloquent 查询以 ->toJson(JSON_PRETTY_PRINT);
结尾,这么做会把对象数据通过 eloquent 序列化成一个格式化的 JSON,并用状态码 200
返回。
同样,这个方法也被绑定到了 api/students
,因为我们之前在路由文件 routes/api.php
里写了:
...
Route::get('students', 'ApiController@getAllStudents');
...
测试
假设我们的应用已经在后台运行了,我们在 Postman 里发一个 GET
请求到 /api/students
。
如上图所示,我们的 API 返回了数据库里所有的数据。
返回单条学生数据
我们再来创建一个只返回一条学生数据的接口。首先我们要到 ApiController
里的 getStudent
方法里。
public function getStudent($id) {
// logic to get a student record goes here
}
我们会通过学生的 id
来取得这个学生的数据,我们仍然使用 eloquent 查询来做:
...
class ApiController extends Controller
{
...
public function getStudent($id) {
if (Student::where('id', $id)->exists()) {
$student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
return response($student, 200);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
...
}
上面这块代码首先检查了这个 id
所代表的学生是否存在。如果存在的话,那就从数据库里通过 eloquent 把他查出来,并用一个 JSON 和 200
的状态码返回。如果传入的 id
在数据库里没查到,那就会返回一个 student not found
消息以及 404
的状态码。
还是一样的,这个方法被绑定到了 api/students/{id}
,因为我们之前在路由文件 routes/api.php
里写了:
...
Route::get('students/{id}', 'ApiController@getStudent');
...
测试
我们在 Postman 里发一个 GET
请求到 /api/students{id}
这个 API,这个 {id}
要是一个数据里已经存在的数据。
如上图所示,我发了一个请求到 http://api-project.test/api/students/3
并且这个 id
所代表的学生数据拿到了。然后我们来试试看如果 id
不存在的情况。
如上图所示,我们查了一个 id
为 100
的,并不存在的学生数据,我们的 API 返回了错误信息,以及一个 404
状态码。
更新一条学生数据
我们再来创建一个更新已有学生数据的 API,我们来到 ApiController
里的 updateStudent
方法。
public function updateStudent(Request $request, $id) {
// logic to update a student record goes here
}
要做到更新,我们需要先检查要更新的数据是否已经存在。如果已经存在的话,那我们就更新这条 id
一样的数据,返回状态码 204
。否则,我们应该返回一条表示更新对象没找到的信息,以及状态码 404
。
public function updateStudent(Request $request, $id) {
if (Student::where('id', $id)->exists()) {
$student = Student::find($id);
$student->name = is_null($request->name) ? $student->name : $request->name;
$student->course = is_null($request->course) ? $student->course : $request->course;
$student->save();
return response()->json([
"message" => "records updated successfully"
], 200);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
我们还在代码里加了校验来针对你只想更新 name
或者 course
中某一条数据的情况。你可以看到当请求进来的时候,会检查 name
和 course
是否为 null。如果是 null 的话那就直接用现有的值。如果不是 null 的话,才会进行更新。这些都是通过三元操作符完成的。
注意:三元操作符的格式是 条件 ? true : false
这个方法也绑到了 api/students/{id}
路由上,因为我们之前在路由文件 routes/api.php
里写了:
...
Route::put('students/{id}', 'ApiController@updateStudent');
...
测试
要测试这个API,我们首先要发一个 GET
请求到 /api/students/1
并且拿到这个 id
为 1 的学生的数据。
返回了这样一条数据:
[
{
"id": 1,
"name": "Michael Okoh",
"course": "Computer Science",
"created_at": "2019-02-08 14:11:17",
"updated_at": "2019-02-08 14:11:17"
}
]
然后我们通过给 api/students/1
发一个 PUT
请求,来把 course
字段改成 "Software Engineering"。要想发送 PUT
请求,你必须要传入一个 JSON payload 给 form-data
。我们顺便把 name
也改成 Trojan Okoh
好了。
{
"name": "Trojan Okoh",
"course": "Software Engineering"
}
上面这段代码就是我们用来更新数据的 JSON payload。 打开 Postman 改成 raw
,并且把类型改成 JSON(application/json)
然后把我们的 JSON payload 写进下面的文本框,并发出 PUT
请求:
如上图所示,我们的 API 返回了成功的消息。我们再给 /api/students/1
发一个 GET
请求确认一下好了:
删除一条学生数据
最后,要删除一条学生数据,我们来到 ApiController
里的 deleteStudent
方法里
public function deleteStudent ($id) {
// logic to delete a student record goes here
}
通过使用 eloquent,我们检查要删除对应 id
所代表的的数据是否存在。如果存在的话我们就删,如果不存在我们就返回一个 not found
的消息和一个 404
状态码。
...
class ApiController extends Controller
{
...
public function deleteStudent ($id) {
if(Student::where('id', $id)->exists()) {
$student = Student::find($id);
$student->delete();
return response()->json([
"message" => "records deleted"
], 202);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
}
这个方法也已经绑定到了 api/students/{id}
,因为我们之前在路由文件 routes/api.php
里写了:
...
Route::delete('students/{id}', 'ApiController@deleteStudent');
测试
要测试这条 API, 我们要列出来我们当前数据库里所有的数据,我们发一个 GET
请求到 /api/students
:
然后我们发出 DELETE
请求到 students/{id}
,其中 {id}
就是我们要删除的数据的 id
。 我选择删除 id
为 2
的数据。
我们的 API 返回了一条成功的信息和一个状态码 202
,所以我们删除成功了。要确认这条数据真的被删除了,我再发个请求到 /api/students
来看看数据库里所有的学生数据吧:
你可以看到在上图中,id
为 2
的学生数据不见了。我们还可以通过给 /api/students/{id}
来发一个 GET
请求,并带上数据 id
为 2
,果然返回了 404
表示这条数据没有找到。
总结
文章写到这里,差不多要结束了,列一下重要的代码文件给你吧。
app\http\controllers\ApiController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Student;
class ApiController extends Controller
{
public function getAllStudents() {
$students = Student::get()->toJson(JSON_PRETTY_PRINT);
return response($students, 200);
}
public function createStudent(Request $request) {
$student = new Student;
$student->name = $request->name;
$student->course = $request->course;
$student->save();
return response()->json([
"message" => "student record created"
], 201);
}
public function getStudent($id) {
if (Student::where('id', $id)->exists()) {
$student = Student::where('id', $id)->get()->toJson(JSON_PRETTY_PRINT);
return response($student, 200);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
public function updateStudent(Request $request, $id) {
if (Student::where('id', $id)->exists()) {
$student = Student::find($id);
$student->name = is_null($request->name) ? $student->name : $request->name;
$student->course = is_null($request->course) ? $student->course : $request->course;
$student->save();
return response()->json([
"message" => "records updated successfully"
], 200);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
public function deleteStudent ($id) {
if(Student::where('id', $id)->exists()) {
$student = Student::find($id);
$student->delete();
return response()->json([
"message" => "records deleted"
], 202);
} else {
return response()->json([
"message" => "Student not found"
], 404);
}
}
}
routes/web.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::get('students', 'ApiController@getAllStudents');
Route::get('students/{id}', 'ApiController@getStudent');
Route::post('students, 'ApiController@createStudent');
Route::put('students/{id}', 'ApiController@updateStudent');
Route::delete('students/{id}', 'ApiController@deleteStudent');
app/Student.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $table = 'students';
protected $fillable = ['name', 'course'];
}
总结
现在你已经有能力使用 Laravel 来构建一组简单的 CRUD RESTful API 了。本文虽然涵盖了文章主题的一些基础知识,但我没有介绍请求验证以及 API 安全性的问题,不过,这可以作为你进一步研究的绝佳选择。