php artisan make:middleware CheckAge
这个命令会在 app/Http/Middleware 目录下创建一个新的中间件类 CheckAge,在这个中间件中,我们只允许提供的 age 大于 200 的请求才能访问应用该中间件的路由,否则,我们会将用户重定向到 / URI:
namespace App\Http\Middleware;
use Closure;
class CheckAge
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->age <= 200) {
return redirect('/');
}
return $next($request);
}
}
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware
{
public function handle($request, Closure $next)
{
// Perform action 请求之前
return $next($request);
}
}
namespace App\Http\Middleware;
use Closure;
class AfterMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
// Perform action 请求之后
return $response;
}
}
中间件分三类,分别是全局中间件、中间件组(如web、api)和指定路由中间件(如auth),他们在 app/Http/Kernel.php 文件中被注册
// 在 App\Http\Kernel 类中...
/**
* 应用的路由中间件列表
*
* 这些中间件可以分配给路由组或者单个路由
*
* @var array
*/
protected $routeMiddleware = [
'age' => \App\Http\Middleware\CheckAge::class,
];
Route::get('/hello', function () {
//do something
})->middleware('age', 'auth');
下面也可以,但是不推荐。
use App\Http\Middleware\CheckAge;
Route::get('admin/profile', function () {
//do something
})->middleware(CheckAge::class);
分配中间件到路由群组时,你可能偶尔需要阻止中间件被应用到群组中的单个路由,这可以通过使用 withoutMiddleware 方法来实现
Route::middleware('age','auth')->group(function () {
Route::get('/', function () {
//
});
// 该路由不会应用 CheckAge 中间件
Route::get('admin/profile', function () {
//
})->withoutMiddleware('age');
});
额外的中间件参数会在 $next 参数之后传入中间件:
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* 处理输入请求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $role
* @return mixed
* translator http://laravelacademy.org
*/
public function handle($request, Closure $next, $role, $age)
{
if ($request->user()->hasRole($role) && $request->user()->age > $age) {
// do something
}
return $next($request);
}
}
中间件参数可以在定义路由时通过 : 分隔中间件名和参数名来指定,多个中间件参数可以通过逗号分隔:
Route::get('post/{id}', function ($id) {
//
})->middleware('role:editor,18');
跨站请求伪造(CSRF)是一种通过伪装授权用户的请求来攻击授信网站的恶意漏洞
<form method="POST" action="/profile">
@csrf
</form>
可以在 VerifyCsrfToken 中间件中将要排除的 URL 添加到 $except 属性数组
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* 从 CSRF 验证中排除的 URL
*
* @var array
*/
protected $except = [
'alipay/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
除了将 CSRF 令牌作为 POST 参数进行验证外,还可以通过设置 X-CSRF-Token 请求头来实现验证,VerifyCsrfToken 中间件会检查 X-CSRF-TOKEN 请求头。实现方式如下,首先创建一个 meta 标签并将令牌保存到该 meta 标签
<meta name="csrf-token" content="{{ csrf_token() }}">
然后在 js 库(如 jQuery)中添加该令牌到所有请求头,这为基于 AJAX 的请求提供了简单、方便的方式来避免 CSRF 攻击:
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
所有的 Laravel 控制器应该继承自 Laravel 自带的控制器基类 App\Http\Controllers\Controller(基类控制器提供了很多便捷的方法供子类使用,比如 middleware 等)
php artisan make:controller UserController
如果你想要定义一个只处理一个动作的控制器,可以在这个控制器中定义 __invoke 方法:
namespace App\Http\Controllers;
use App\Models\User;
use App\Http\Controllers\Controller;
class ShowProfile extends Controller
{
/**
* 展示给定用户的个人主页
*
* @param int $id
* @return Response
*/
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
当你为这个单动作控制器注册路由的时候,不需要指定方法:
Route::get('user/{user}',\App\Http\Controllers\ShowProfile::class);
这背后的原理是在 PHP 中当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
你可以通过以下 Artisan 命令快速创建单一动作控制器,只需带上 --invokable 选项即可:
php artisan make:controller ShowProfile --invokable
在控制器的构造函数中设置中间件更方便,你可以使用基类提供的 middleware 方法轻松分配中间件给该控制器的动作,你甚至可以限制中间件只应用到该控制器类的指定方法,由于闭包函数在真正使用的时候才会执行,所有这个功能可用于在控制器中获取 Session 数据(众所周知,在 Laravel 控制器中不能直接获取 Session 数据)
class UserController extends Controller
{
/**
* Instantiate a new controller instance.
*
* @return void
*/
public function __construct()
{
// auth 中间件会应用到所有方法
$this->middleware('auth');
// log 中间件只会应用到 index 方法
$this->middleware('log')->only('index');
// subscribed 中间件会应用到 store 之外的所有方法
$this->middleware('subscribed')->except('store');
// 使用闭包注册中间件,这为我们定义只在某个控制器使用的中间件提供了方便(无需定义完整的中间件类)
$this->middleware(function ($request, $next) {
// do something
return $next($request);
});
}
}
Laravel 的资源控制器可以让我们很便捷地构建基于资源的 RESTful 控制器
php artisan make:controller PostController --resource
如果你使用了路由模型绑定,并且想要在资源控制器的方法中对模型实例进行依赖注入,可以在生成控制器的使用使用 --model 选项:
php artisan make:controller PostController --resource --model=Post
这个控制器包含了每一个资源操作对应的方法:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* 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 int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
可以通过 Route 的 resource 方法为该控制器注册一个资源路由:
Route::resource('posts', \App\Http\Controllers\PostController::class);
你可以通过传递数组到 resources 方法从而一次注册多个资源控制器:
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Route::resource('posts', PostController::class, ['only' =>
['index', 'show']
]);
Route::resource('posts', PostController::class, ['except' =>
['create', 'store', 'update', 'destroy']
]);
请求方式 | URI路径 | 控制器方法 | 路由名称 | 用途 |
---|---|---|---|---|
GET | /posts | index | posts.index | 获取所以资源的信息 |
GET | /posts/create | create | posts.create | 获取创建资源的页面 |
POST | /posts | store | posts.store | 提交创建资源的申请 |
GET | /posts/{post} | show | posts.show | 获取某个特定的资源的信息 |
GET | /posts/{post}/edit | edit | posts.edit | 获取编辑某个特定资源的编辑页面 |
PUT/PATCH | /posts/{post} | update | posts.update | 提交某个特定资源的更新申请 |
DELETE | /posts/{post} | destroy | posts.destroy | 提交某个资源的删除申请 |
声明被 API 消费的资源路由时,你可能需要排除展示 HTML 模板的路由,如 create
和 edit
,为了方便起见,Laravel 提供了 apiResource 方法自动排除这两个路由:
Route::apiResource('posts', PostController::class);
同样,你可以传递数组到 apiResources 方法以便一次注册多个 API 资源控制器:
Route::apiResources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
要想快速生成不包含 create 或 edit 方法的 API 资源控制器,可以在执行 make:controller 命令时使用 --api 开关:
php artisan make:controller API/PostController --api