随着移动端和JavaScript框架的发展,比如React和Vue,Restful风格的API越来越流行。使用Restful风格的好处就是一个后端程序可以与多个版本的前端用户界面关联。
Laravel提供了创建Rest API的环境和生态。
首先得导入依赖包比如Laravel Passport和Laravel Sanctum,这两个包提供了Restful API的权限功能,并且使用起来非常简单。
Laravel Breeze包提供了重置密码的模版功能。
Socialite和Scout提供了登陆和查找功能。
Laravel生态圈提供了程序员开发程序时遇到问题的所有解决方案,为开发人员提供最大的开发效率。
此篇教材展示了如何创建Laravel Rest API,并且使用Sanctum进行权限认证。
Resful API是什么?
Resful其实是一种传输状态,是应用间的一种交流方式,这种方式使用了HTTP协议。这种方式是无状态的,短连接的方式,并且不存储seswsion。每个请求都需要像新请求的情况进行处理。
Restful API的好处就是他方便缓存。返回的数据从Redis或Memcached中获取是非常方便的。
一个API是否是Restful分割的,需要满足如下2点:
- 能够使用URL或Endpoint进行访问;
- 能够使用Restful方法;
- 使用HTTP头;
- 必须返回有效的相应diam
通常Restful方法有如下几种:
GET:获取数据;
POST:新增数据;
PUT/PATCH:更新数据;
DELETE:删除数据;
如何使用Laravel创建REST API
首先创建一个Laravel应用。
laravel new rest
然后创建model和migration,在这个实例中,使用Products来代表资源。
php artisan make:model Products -m
-m标签会让Laravel去创建Products模型对应的数据库迁移文件。
下面是模型文件:
- //App/Models/Products
- <?php
-
- namespace App\Models;
-
- use Illuminate\Database\Eloquent\Factories\HasFactory;
- use Illuminate\Database\Eloquent\Model;
-
- class Products extends Model
- {
-
- use HasFactory;
-
- }
下面是模型文件对应的数据库迁移文件:
- <?php
-
- use Illuminate\Database\Migrations\Migration;
- use Illuminate\Database\Schema\Blueprint;
- use Illuminate\Support\Facades\Schema;
-
- return new class extends Migration
- {
- /**
- * Run the migrations.
- *
- * @return void
- */
- public function up()
- {
- Schema::create('products', function (Blueprint $table) {
- $table->id();
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('products');
- }
- };
这里需要手动更新下迁移文件,在文件中添加字段也就是列,这也数据库中也会有对应的字段和列。在Products这个表中添加name、price、description。
- <?php
-
- use Illuminate\Database\Migrations\Migration;
- use Illuminate\Database\Schema\Blueprint;
- use Illuminate\Support\Facades\Schema;
-
- return new class extends Migration
- {
- /**
- * Run the migrations.
- *
- * @return void
- */
- public function up()
- {
- Schema::create('products', function (Blueprint $table) {
- $table->id();
- $table->string('name');
- $table->double('price');
- $table->longText('description');
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('products');
- }
- };
随后在Products模型中将name、price、description注册下。好处是使得数据库字段名和类名一一对应,这样能有效的防止SQL注入,比较安全。
- //App/Models/Products
- <?php
-
- namespace App\Models;
-
- use Illuminate\Database\Eloquent\Factories\HasFactory;
- use Illuminate\Database\Eloquent\Model;
-
- class Products extends Model
- {
- use HasFactory;
-
- protected $fillable = [
- 'name', 'price', 'description'
- ];
- }
随后在.env文件中添加数据库认证信息:
- DB_CONNECTION=mysql
- DB_HOST=127.0.0.1
- DB_PORT=3306
- DB_DATABASE=laravel-rest
- DB_USERNAME=root
- DB_PASSWORD=password
最后一步是将Products表迁移到数据库里面:
php artisan migrate
创建数据库Seeder和Factory
在开发的时候需要一些虚拟数据,这也程序员开发起来就比较方便快捷。Laravel提供了Factory facade,并且使用Faker生成仿真数据。
使用下面的命令创建Factory。
php artisan make:factory ProductsFactory
这个文件会在databases/factories文件夹被创建。
这里需要更新下这个文件,改成如下:
- //database/factories/ProductsFactory
- <?php
-
- namespace Database\Factories;
-
- use Illuminate\Database\Eloquent\Factories\Factory;
-
- /**
- * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Products>
- */
- class ProductsFactory extends Factory
- {
- /**
- * Define the model's default state.
- *
- * @return array<string, mixed>
- */
- public function definition()
- {
- return [
- 'name' => $this->faker->word,
- 'price' => $this->faker->numberBetween(1, 99),
- 'description' => $this->faker->sentence()
- ];
- }
- }
现在factory已经准备好了,下面就是在DatabaseSeeder文件中进行seed。
- //database/seeders/DatabaseSeeder
- <?php
-
- namespace Database\Seeders;
-
- // use Illuminate\Database\Console\Seeds\WithoutModelEvents;
- use Illuminate\Database\Seeder;
-
- class DatabaseSeeder extends Seeder
- {
- /**
- * Seed the application's database.
- *
- * @return void
- */
- public function run()
- {
- \App\Models\Products::factory(10)->create();
- }
- }
使用下面的命令进行seed。
php artisan db:seed
创建控制器
下面创建Products的控制器,这个控制器里面需要编写对于的逻辑,使用下面的命令进行创建。
php artisan make:controller ProductsController -r
-r参数代表resourceful,他会创建http相关的通用请求。
请求包括index、show、store、update、destory,对于这些方法,可以删除、创建、编辑他们,他们并不是都需要,也并不是非要这样命名。下面将Products控制器写成如下。
- //App/Http/Controllers/ProductsController
-
- <?php
-
- namespace App\Http\Controllers;
-
- use App\Http\Resources\ProductResource;
- use App\Models\Products;
- use Illuminate\Http\Request;
-
- class ProductsController extends Controller
- {
- /**
- * Display a listing of the resource.
- *
- * @return \Illuminate\Http\Response
- */
- public function index()
- {
- //
- }
-
-
- /**
- * Store a newly created resource in storage.
- *
- * @param \Illuminate\Http\Request $request
- * @return \Illuminate\Http\Response
- */
- public function store(Request $request)
- {
- //
- }
-
- /**
- * Display the specified resource.
- *
- * @param Products $product
- * @return \Illuminate\Http\Response
- */
- public function show(Products $product)
- {
- //
- }
-
-
- /**
- * Update the specified resource in storage.
- *
- * @param \Illuminate\Http\Request $request
- * @param Products $product
- * @return \Illuminate\Http\Response
- */
- public function update(Request $request, Products $product)
- {
- //
- }
-
- /**
- * Remove the specified resource from storage.
- *
- * @param Products $product
- * @return \Illuminate\Http\Response
- */
- public function destroy(Products $product)
- {
- //
- }
- }
上面的方法和HTTP中的get、post、patch/put、delete对应。
Index(获取所有产品)
此方法中返回数据库中的所有products:
- use App\Models\Products;
- public function index()
- {
- return Products::all();
- }
Show(获取一个产品)
此方法从数据库中获取一个产品,并返回给前端。
通过产品id这个参数获取指定的产品
注意:下面使用的ProductResource()函数,是获取指定产品的,在文章后面将会给出这个函数的详细代码:
- use App\Http\Resources\ProductResource;
- use App\Models\Products;
-
- public function show(Products $product)
- {
- return new ProductResource($product);
- }
Store(新增一条产品记录)
使用此方法在数据库中新增一记录,这里使用的是HTTP的post方法。代码如下:
- use App\Http\Resources\ProductResource;
- use App\Models\Products;
-
- public function store(Request $request)
- {
- $product_name = $request->input('name');
- $product_price = $request->input('price');
- $product_description = $request->input('description');
-
- $product = Products::create([
- 'name' => $product_name,
- 'price' => $product_price,
- 'description' => $product_description,
- ]);
- return response()->json([
- 'data' => new ProductResource($product)
- ], 201);
- }
Update(更新产品信息)
更新产品的名字、价格、描述信息,具体的逻辑代码如下:
- use App\Http\Resources\ProductResource;
- use App\Models\Products;
-
- public function update(Request $request, Products $product)
- {
- $product_name = $request->input('name');
- $product_price = $request->input('price');
- $product_description = $request->input('description');
-
- $product->update([
- 'name' => $product_name,
- 'price' => $product_price,
- 'description' => $product_description,
- ]);
- return response()->json([
- 'data' => new ProductResource($product)
- ], 200);
- }
Destroy(删除一条产品记录)
如下代码所示:
- use App\Models\Products;
-
- public function destroy(Products $product)
- {
- $product->delete();
- return response()->json(null,204);
- }
Routes&EndPoints
下面创建EndPoints,这样就能进行HTTP访问了,在routes/api.php中新增如下代码:
- //routes/api.php
- <?php
-
- use App\Http\Controllers\ProductsController;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\Route;
-
- /*
- |--------------------------------------------------------------------------
- | 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:sanctum')->get('/user', function (Request $request) {
- return $request->user();
- });
-
- Route::get('products', [ProductsController::class, 'index'])->name('products.index');
- Route::get('products/{product}', [ProductsController::class, 'show'])->name('products.show');
- Route::post('products', [ProductsController::class, 'store'])->name('products.store');
- Route::put('products/{product}', [ProductsController::class, 'update'])->name('products.update');
- Route::delete('products/{product}', [ProductsController::class, 'destroy'])->name('products.destroy');
上面的Endpoints和ProductsController中方法是对应的。
下面测试下ProductsController中的index函数,是HTTP的Get请求,返回值如下:
- [
- {
- "id": 1,
- "name": "quo",
- "price": 15,
- "description": "Ut rerum aut deleniti eveniet ad et ullam perferendis.",
- "created_at": "https://files.jxasp.com/image/2022-11-18T15:18:13.000000Z",
- "updated_at": "https://files.jxasp.com/image/2022-11-18T15:18:13.000000Z"
- },
- {
- "id": 2,
- "name": "maxime",
- "price": 70,
- "description": "Natus officiis repellat vero ea voluptatem mollitia similique.",
- "created_at": "https://files.jxasp.com/image/2022-11-18T15:18:13.000000Z",
- "updated_at": "https://files.jxasp.com/image/2022-11-18T15:18:13.000000Z"
- }
- ]
格式化响应
上面的响应是以Json格式返回的,内容包括了数据库中的所有列。
如果想返回指定的列。比如不需要返回created_at,update_at,以及关于商品打折的信息。所有就需要定制下响应。创建响应类:
php artisan make:resource ProductResource
改变下返回数组。
- //App/Http/Resources/ProductResource
- <?php
-
- namespace App\Http\Resources;
-
- use Illuminate\Http\Resources\Json\JsonResource;
-
- class ProductResource extends JsonResource
- {
- /**
- * Transform the resource into an array.
- *
- * @param \Illuminate\Http\Request $request
- * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
- */
- public function toArray($request)
- {
- return [
- 'id' => $this->id,
- 'product_name' => $this->name,
- 'product_price' => "$" . $this->price,
- 'discounted_price' => "$" . ($this->price * 0.9),
- 'discount' => "$" . ($this->price * 0.1),
- 'product_description' => $this->description,
- ];
- }
- }
再到ProductsController中index更新成如下代码:
- public function index()
- {
- return ProductResource::collection(Products::all());
- }
返回新的响应内容如下:
- {
- "data": [
- {
- "id": 1,
- "product_name": "quo",
- "product_price": "$15",
- "discounted_price": "$13.5",
- "discount": "$1.5",
- "product_description": "Ut rerum aut deleniti eveniet ad et ullam perferendis."
- },
- {
- "id": 2,
- "product_name": "maxime",
- "product_price": "$70",
- "discounted_price": "$63",
- "discount": "$7",
- "product_description": "Natus officiis repellat vero ea voluptatem mollitia similique."
- }
- ]
- }
Response Codes
每一个响应最好都带有一个序列。这样客户端可以知道当前服务端的状态。
一般的相应码如下:
- 200-OK,响应正常;
- 201-Created,通过在Post请求里面,代表资源创建成功;
- 204-No Content,无数据返回,通常在删除资源的时候用;
- 400-Bad Request,用户提交的密码或参数不正确;
- 401-Unauthorized,用户无权限,需要身份验证;
- 403-Forbidden,用户权限不够,禁止访问;
- 404-Not Found,无对应的此资源;
- 500-Internal Server Error,服务端内部错误。
Laravel使用jsonse->json()函数可以带个响应码。代码如下:
response->json(data,status code)