关于拾年之璐:
微信公众号:知行校园汇,点击查看,欢迎关注
其他平台(点击蓝字可访问):
GitHub | Gitee | 哔哩哔哩 | 语雀 | 简书 | 微信小程序 | 知行达摩院
本文专栏: Laravel,点击查看系列文章
3.1 响应设置
路由和控制器处理完业务都会返回一个发送到浏览器的响应:return
。几种常用的返回格式如下所示:
如果是字符串,会直接输出。
如果是数组,则会输出json
格式,本身是 Response
对象。示例:
public function hello3()
{
return [1, 2, 3]; //输出json 格式
// return response([1, 2, 3]); //同上
// return response()->json([1, 2, 3]); //同上
}
结果:
如果使用 response()
输出的话,可以设置状态码和响应头信息:
public function hello3()
{
return response([1, 2, 3], 400); //状态码:400
}
结果:
也可以给 HTTP 添加或修改标头
,比如将html 解析模式改成文本plain 模式:
public function hello3()
{
return response('<br>这里是文本<br>', 201)->header('Content-Type', 'text/plain');
}
输出结果:
结合上面的响应操作,再结合 view()
视图功能,显示纯HTML 代码页面:
public function hello3()
{
return response()
->view('hello', ['name' => '张三', 'id' => '1001'])
->header('Content-Type', 'text/plain');
}
结果:
3.2 资源控制器
资源控制器
是某个特定场景下的产物。比如在博客系统中,默认有帖子留言等类型的接口,该资源控制器可以默认生成所有的接口。这是一种专门处理CURD(增删改查)的控制器,方法很多且方法名基本固定。
可以手动创建,不过比较复杂,推荐使用命令直接创建:
php artisan make:controller BlogController --resource
这时,该命令会创建一个名为 BlogController
的控制器,其初始状态如下:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class BlogController 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)
{
//
}
}
如上,生成的控制器产生了7个方法。
然后,使用下面的一条命令,即可生成所有的路由:
Route::resource('blogs', 'BlogController'); //单个资源路由
如果有多个资源控制器,可以使用下面的命令批量定义:
Route::resources([
'blogs' => 'BlogController',
'comments' => 'CommentController'
]);
怎么看是否已生成路由?使用下面的命令可以查看:
php artisan route:list
结果:
如果我们注册了资源路由,那么如上图的资源路由URI 和名称均自动创建生效。
这里注意,默认生成的控制器,注释中的返回类型为 @return \Illuminate\Http\Response
,测试的时候,可以修改为string
。
然后在每个路由的控制器中,写入一些基本的测试信息,如:
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
return response('index');
}
我们也可以限制资源路由只开放部分方法或排除部分方法。
示例:只有index()
, show()
可访问
//单个资源路由
Route::resource('blogs', 'BlogController')
->only(['index','show']);
结果:
示例:排除 index()
, show()
的其它方法可访问
//单个资源路由
Route::resource('blogs', 'BlogController')
->except(['index','show']);
结果:
资源控制器还有一种不需要 HTML 页面方法的API 路由
,只提供数据接口。
这种类型的资源控制器,可以使用如下命令创建:
php artisan make:controller CommentController --api
这时,创建的控制类,初始内容如下:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CommentController 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 int $id
* @return \Illuminate\Http\Response
*/
public function show($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)
{
//
}
}
API接口
,并不需要HTML 页面(create,edit)。
然后路由的定义如下:
Route::apiResource('comments', 'CommentController');
结果:
3.3 资源嵌套
资源嵌套
,是指两个控制类,通过路由绑定在一起。
首先创建两个资源控制器:
php artisan make:controller BlogController --resource
php artisan make:controller CommentController --resource
然后,使用嵌套资源路由,即可实现以上两个控制器的嵌套:
Route::resource('blogs.comments', 'CommentController');
结果:
以上需求,可以通过嵌套资源路由来实现这个功能。
编辑 CommentController
类中的 edit()
方法以及传参如下:
/**
* Show the form for editing the specified resource.
*
* @param $blog_id
* @param $comment_id
* @return string
*/
public function edit($blog_id, $comment_id)
{
//
return '编辑博文下的评论,博文id:'.$blog_id.',评论id:'.$comment_id;
}
结果:
在实际项目中,每个 id
都是独立唯一的,并不需要父id 和子id 同时存在。
所以为了优化资源嵌套,通过 路由方法->shallow()
实现浅层嵌套方法。格式:
Route::resource('blogs.comments', 'CommentController')->shallow();
结果:
然后修改前往的 edit方法
:
public function edit($id)
{
//
return '编辑博文下的评论id:'.$id;
}
结果:
3.4 自定义路由
如果觉得默认的资源控制器的资源路由命名过长,可以自己自定义,有两种方式。
一种是修改单一的方法路由:
Route::resource('blogs.comments', 'CommentController')
->name('index','b.c.i');
结果:
另一种是修改多个路由
:
Route::resource('blogs.comments', 'CommentController')
->names([
'index'=>'b.c.i',
'store'=>'b.c.s'
]);
结果:
然后,就可以在控制类、路由中,调用Name比较短的名字。
如果觉得资源路由的参数不符合你的心意,也可以改变:
Route::resource('blogs.comments', 'CommentController')
->name('index','b.c.i')
->parameter('blogs','id');
未改变,访问的方式是:
route('b.c.i', ['blogs' => 10]);
改变后,访问的方式是:
route('b.c.i', ['id' => 10]);
当然,也可以对多个参数进行修改,如:
Route::resource('blogs.comments', 'CommentController')
->parameters([
'blogs' => 'blog_id',
'comments' => 'comment_id'
]);
这时,访问 blogs.comments.edit
的路由,访问方式是:
return route('blogs.comments.edit', ['blog_id' => 10, 'comment_id' => 1000]);
3.5 表单伪造和CSRF保护
之前的数据,都是通过GET请求获取的。而表单的数据提交,是通过POST请求实现。
首先在TaskController下创建两个方法。
一个是表单页:
public function form()
{
return view('form');
}
表单页路由:
Route::get('task/form', 'TaskController@form');
然后创建表单页面,内容为:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>表单</title>
</head>
<body>
<form action="/task/getform" method="post">
<input type="hidden" name="_token" value="{{csrf_token()}}">
用户名: <input type="text" name="user">
<button type="submit">提交</button>
</form>
</body>
</html>
另一个是接受表单的数据路由:
Route::post('task/getform', function (){
echo '接收数据:'.Request::input('user');
echo '<br>';
echo '请求方法:'.Request::method();
});
执行结果为:
在这个简短的实例中,为了避免被跨站请求伪造攻击,框架提供了CSRF 令牌保护
,请求时验证:
<input type="hidden" name="_token" value="{{csrf_token()}}">
表单可以实现POST
提交方式,那其它提交方式该如何实现呢?可以采用伪造技术:
<input type="hidden" name="_method" value="PUT">
在form里增加这一行,然后把请求的方法,由post修改为any,再次请求,结果为:
对于CSRF 令牌保护和表单伪造提交方式,也支持快捷方式的声明,如下:
@csrf
@method('PUT')
完整:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>表单</title>
</head>
<body>
<form action="/task/getform" method="post">
@csrf
@method('PUT')
用户名: <input type="text" name="user">
<button type="submit">提交</button>
</form>
</body>
</html>
我们可以设置全局 取消CSRF验证
,把下图所示注释掉即可:
但是,不推荐这样。
我们可以只将某些URL 关闭csrf 验证
,通过设置csrf 白名单的方式实现。
白名单具体设置位置在:中间件目录下的VerifyCsrfToken.php
文件,可以写单个的路由,也可以使用通配符,设置一系列路由:
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
// 在这里添加关闭CSRF的路由
'api/*',
];
}
以上。