用 Laravel 构建 RESTful API 详细教程

卡拉先生
发布于 2020年09月06日 | 上次编辑:2020年09月07日

Building and Consuming a RESTful API in Larave width 808

不管是你平时用的各种 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 删除一个学生。

每条学生数据仅包含 namecourse 作为详情。当你开发好这些接口以后,你要使用这些接口来完成一个学生信息管理应用。

设置好 Laravel 应用

首先,创建一个 Laravel 应用。你可以在终端执行这行命令:

$ laravel new api-project

然后进入你刚创建的项目:

$ cd api-project

启动 Laravel 服务器:

$ php artisan serve

然后你就可以通过 https://localhost:8000 访问到你的应用了

server

现在我们创建一个数据库,在终端输入:

$ mysql -uroot -p

如果你还没登陆过的话,你会被要求输入你的MySQL密码。接着,通过这个命令我们来创建一个叫 api-project 的新数据库:

CREATE DATABASE `api-project`;

new db

我们继续创建一个 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 文件夹里会生成一个数据迁移文件。你需要修改这个数据迁移文件,给它创建两列接受字符串的字段 namecourse

...
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 这个选项,并把值如图所示地传进去:

test1

如果成功的话,它会返回成功消息以及一个 201 的状态码。现在你再多创建点学生数据进去吧,下一个环节会用到。

返回所有的学生数据

现在让我们来到 ApiControllergetAllStudents方法

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

test2

如上图所示,我们的 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} 要是一个数据里已经存在的数据。

test3

如上图所示,我发了一个请求到 http://api-project.test/api/students/3 并且这个 id 所代表的学生数据拿到了。然后我们来试试看如果 id 不存在的情况。

test4

如上图所示,我们查了一个 id100 的,并不存在的学生数据,我们的 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 中某一条数据的情况。你可以看到当请求进来的时候,会检查 namecourse 是否为 null。如果是 null 的话那就直接用现有的值。如果不是 null 的话,才会进行更新。这些都是通过三元操作符完成的。

注意:三元操作符的格式是 条件 ? true : false

这个方法也绑到了 api/students/{id} 路由上,因为我们之前在路由文件 routes/api.php 里写了:

...
Route::put('students/{id}', 'ApiController@updateStudent');
...

测试

要测试这个API,我们首先要发一个 GET 请求到 /api/students/1 并且拿到这个 id 为 1 的学生的数据。

test5

返回了这样一条数据:

[
    {
        "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 payloadform-data。我们顺便把 name 也改成 Trojan Okoh 好了。

{
    "name": "Trojan Okoh",
    "course": "Software Engineering"
}

上面这段代码就是我们用来更新数据的 JSON payload。 打开 Postman 改成 raw,并且把类型改成 JSON(application/json)

test6

然后把我们的 JSON payload 写进下面的文本框,并发出 PUT 请求:

test7

如上图所示,我们的 API 返回了成功的消息。我们再给 /api/students/1 发一个 GET 请求确认一下好了:

test8

删除一条学生数据

最后,要删除一条学生数据,我们来到 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

test9

然后我们发出 DELETE 请求到 students/{id},其中 {id} 就是我们要删除的数据的 id。 我选择删除 id2 的数据。

test10

我们的 API 返回了一条成功的信息和一个状态码 202,所以我们删除成功了。要确认这条数据真的被删除了,我再发个请求到 /api/students 来看看数据库里所有的学生数据吧:

test11

你可以看到在上图中,id2 的学生数据不见了。我们还可以通过给 /api/students/{id} 来发一个 GET 请求,并带上数据 id2,果然返回了 404 表示这条数据没有找到。

test12

总结

文章写到这里,差不多要结束了,列一下重要的代码文件给你吧。

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 安全性的问题,不过,这可以作为你进一步研究的绝佳选择。

本文参考

原文链接

MySQL安装

MySQL查看所有用户

SQLite, MySQL, Postgres 对比

友情链接更新日志© 2020, 卡拉搜索, Built with ❤️ in San Francisco + Beijing

京ICP备15049164号-3