HTTP请求和响应

前一篇笔记我们学习了Laravel中控制器的用法,这里我们再仔细了解一下Laravel中的请求(Request)和响应(Response)对象。它们的类全名分别是Illuminate\Http\RequestIlluminate\Http\Response,本篇笔记我们主要学习请求和响应对象的使用方法,有关文件上传下载的内容将在后文介绍。

请求 Request

Laravel对HTTP请求进行了封装,便于我们获取请求参数、请求方法、请求地址、请求头等信息。

请求参数和路径参数

传统的HTTP请求形如student?id=1,REST风格的请求形如students/1,原生PHP对第一种形式已经进行了封装,但是对第二种形式,只能手动解析,比较麻烦。好在大多数Web框架都针对REST风格的请求做了封装。

请求参数

传统类型的请求参数可以通过$request->input()读取。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function show(Request $request)
    {
        $id = $request->input('id');
        return 'id='.$id;
    }
}

如果读取的参数不存在,将返回null

input()也可以接收两个参数,第二个参数为默认值。

另外要注意一点,input()的返回值,只可能是arraystringnull,如果是数字的类型,需要我们自己手动转换。

路由配置中,需要配置对应的请求地址和控制器方法。

Route::get('/users', 'UserController@show');

路径参数

REST风格的路径参数可以通过依赖注入的形式,传递给控制器方法。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function show(int $id)
    {
        return 'id='.$id;
    }
}

路由中,路径配置需要对应的占位符。

Route::get('/users/{id}', 'UserController@show');

获取请求属性

除了请求参数,HTTP请求报文中还包含请求方法、请求URI、请求头等有用的信息,这些都封装在$request对象中。

假设我们使用浏览器访问http://localhost:8000/users?id=1,可以通过如下代码获取请求对象中的各种信息:

// 请求方法
$method = $request->method();
// 请求URI,例如:users
$path = $request->path();
// 请求路径URL,例如:http://localhost:8000/users
$url = $request->url();
// 请求带参数URL,例如:http://localhost:8000/users?id=1
$full_url = $request->fullUrl();
// 请求头
$user_agent = $request->header('User-Agent');

判断属性是否存在

我们代码中使用请求参数前,一般都会检查该参数是否存在。一般的写法是进行if判断,如果其不为null(更进一步的,还可能需要判断不为空字符串),则视为存在。

Laravel对这个功能进行了封装,我们可以使用$request->has()来判断请求参数是否为null$request->filled()来判断请求参数是否非null且非空字符串,下面是一个例子。

if ($request->filled('aa')) {
    // ...
} else {
    abort(400);
}

读取Json请求

除了表单形式的请求参数,和REST风格的路径参数,将请求内容封装成请求体中的Json对象也是常用的方式,尤其是当我们的表单非常复杂,带有很多动态配置的时候。下面JavaScript代码中,我们使用axios上传一个Json对象。

var jsonObj = {
    username: 'Jerry',
    password: 'abc123',
    courseList: [
        {id: 1, name: 'Math'},
        {id: 2, name: 'English'}
    ]
};
axios.post('/test', jsonObj)
    .then((rsp) => {
    console.log(rsp);
});

该请求的Content-Typeapplication/json;charset=utf-8,Laravel会自动识别该请求类型,我们可以直接从注入控制器的$request对象中读取转换到PHP的关联数组。

// 获取请求Json对象
$req_json = $request->input();

文件上传

我们知道HTTP协议中,文件上传使用的请求体格式为multipart/form-data,手动解析是比较麻烦的。但Laravel对此做了封装,只需要用$request->file()读取出来就行了。

$file = $request->file('file');

验证文件是否有效

同请求参数类似,我们也可以用$request->hasFile()来判断文件字段是否存在,以及$request->file('file')->isValid()来判断上传文件是否有效。

if ($request->hasFile('file') && $request->file('file')->isValid()) {
    $file = $request->file('file');
    // ... 对文件的后续处理
} else {
    abort(400);
}

存储文件

存储文件请参考Laravel的文件系统相关章节。

响应 Response

Laravel对HTTP响应也进行了封装,基于后端模板的MVC开发中,HTTP响应通常是一个视图模板,而前后端分离开发中,响应则可能是一段Json。

响应到视图

实际上,各种Web框架反反复复的发明了许多“模板引擎”,让人不胜其烦。Laravel使用的是叫做的Blade模板引擎,相对来说算是比较好用的一个。

下面代码中,index方法会返回一个视图模板,response()->view('demo')中参数字符串对应的就是模板的名字,该模板文件位于resources/views/demo.blade.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{

    public function index()
    {
        return response()->view('demo');
    }
}

当然,模板中肯定是要填充数据的,数据可以通过关联数组的形式进行填充。下面代码中,我们在填充了一个字符串变量。

$data = 'Hello, laravel!';
return response()->view('demo', ['data' => $data], 200);

注:第三个参数是可选的HTTP响应状态码,默认为200,一般可以省略。

模板中直接访问$data即可:

<div>{{$data}}</div>

有关Blade模板的用法,具体将在后文介绍。

输出Json

在前后端分离的开发方式中,控制器的响应一般是Json格式。我们可以用response()->json(),直接将一个关联数组作为参数返回即可。

return response()->json([
    'username' => 'Tom',
    'password' => 'abc123'
]);

Laravel会自动帮我们设置好Content-Type: application/json响应头,告知浏览器响应数据的媒体类型,我们不需要像原生PHP开发一样手动设置了。

响应文件下载

我们知道HTTP实现文件下载,一个是要将数据写到输出流,另外还需要设置Content-Diposition响应头。Laravel对文件下载的这些逻辑做了封装,使用非常简单。

public function downloadLocal(Request $request)
{
    $file_id = $request->input('file_id');
    try {
        return response()->download(storage_path('app/public/avatars/' . $file_id));
    } catch (FileNotFoundException $e) {
        return response('出错了', 500);
    }
}

我们直接使用$response()->download()即可响应文件下载,参数是文件路径,响应头信息会自动添加。

如果返回的是图片、PDF文档等,我们不希望浏览器下载,而是直接加载预览,那直接返回response()->file()就可以了。

return response()->file(storage_path('app/public/avatars/' . $file_id));

这种响应方式不会增加Content-Type: application/json响应头。

重定向

除了直接返回数据,重定向响应也非常常见,例如:登陆成功跳转到网站内部页面,付款完成跳转到订单成功页面等。重定向有多种实现方式:

  1. 使用302状态码和响应头中的Location实现
  2. 根据Ajax响应,使用JavaScript的location.href=xxx进行跳转
  3. 响应一个跳转专用页面,使用JavaScript进行倒计时跳转

第1种方式在基于后端模板的开发中比较常用,第2种在现代的前后端分离开发中更常用(第3种用户体验差,给人感觉也比较low,除非业务逻辑前两种方式真的无法实现,否则不会用到)。

这里Laravel作为一个后端框架,我们介绍的是第一种方式。

重定向到路由路径

下面代码中,我们返回的响应让浏览器重定向到/

return redirect('/');

带参数重定向

如果想在重定向中携带参数,一般是通过Session实现的,但是我们不用手写这个功能,因为Laravel已经帮我们封装好了。我们直接使用redirect()->with()

return redirect('/')->with('key', 'abc123');

在模板中,我们直接读取Session即可:

<div>{{session('key')}}</div>

注:该方式存储在Session中的数据只会保持到下一次请求,以避免污染到除该次重定向外的请求。

重定向到外部路径

在极少情况下,我们可能需要重定向到其它域名。这可以通过redirect()->away()来实现。

return redirect()->away('https://www.google.com');
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap