Response Laravel

Response là một component khá đơn giản trong Laravel thế nhưng chúng ta cũng không thể bỏ qua nó khi học về Laravel được. Bài viết này chủ yếu học cú pháp và các tính năng của nó các bạn nhé!

I. Tạo response (Creating response)

1. Chuỗi và mảng (String and array)

Tất cả các route và controller nên trả về một response để gửi cho trình duyệt người dùng. Laravel cung cấp cho người sử dụng nhiều cách khác nhau để trả về response. Một response đơn giản nhất là trả về chuỗi từ route hoặc controller. Framework sẽ tự động chuyển chuỗi về HTTP response đầy đủ.

Route::get('/', function() {
    return 'Home page';
});

Ngoài việc trả về chuỗi từ route hoặc controller , bạn có thể trả về một mảng. Framrwork sẽ tự động chuyển mảng đó về JSON response.

Route::get('/', function() {
    return [1, 2, 3];
});

2. Đối tượng response (Response object)

Thông thường thì bạn sẽ không trả về các chuỗi hoặc mảng đơn giản trong ứng dụng. Thay vào đó bạn sẽ trả về lớp khởi tạo đầy đủ Illuminate\Http\Response hoặc view.

Trả về lớp khởi tạo đầy đủ Response cho phép chúng ta tùy chỉnh mã trạng thái (status code) của HTTP response và header. Lớp Response này kế thừa từ class Symfony\Component\HttpFoundation\Response, cung cấp nhiều method cho việc xây dựng HTTP response.

Chẳng hạn:

Route::get('home', function () {
    return response('Hello World', 200)
                ->header('Content-Type', 'text/plain');
});

Chúng ta đã thiết lập status code HTTP response là 200 thông qua tham số thứ hai của method response. Ngoài ra còn đính kèm header Content-Type: text/plain bằng phương thức header kế tiếp.

3. Đính kèm header vào response (Attaching header to response)

Cần nhớ rằng tất cả các method response có thể kết nối được, cho phép bạn xây dựng bất kỳ trường hợp response nào. Chẳng hạn bạn có thể thêm hàng loạt header với method header trước khi trả về người dùng.

return response($content)
            ->header('Content-Type', $type)
            ->header('X-Header-One', 'Header Value')
            ->header('X-Header-Two', 'Header Value');

Hoặc thay vì kết nối nhiều method như vậy, chúng ta có thể sử dụng method withHeaders để truyền nhiều header dưới dạng mảng.

return response($content)
            ->withHeaders([
                'Content-Type' => $type,
                'X-Header-One' => 'Header Value',
                'X-Header-Two' => 'Header Value',
            ]);

4. Cache controll middleware

Laravel cung cấp cache.headers middleware, cung cấp cú pháp nhanh để thiết lập Cache-Controll header cho một nhóm route. Nếu etag được liệt kê trong cú pháp thì hàm băm MD5 của response sẽ tự động đặt cho ETag identifier.

Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function() {
    Route::get('privacy', function () {
        // ...
    });

    Route::get('terms', function () {
        // ...
    });
});

Nếu bạn nào vẫn chưa biết gì về Cache-Control thì công dụng của nó là để:

  • Tối ưu tốc độ tải trang (cache các ảnh lớn, file JS, file CSS...)
  • Tăng tính bảo mật (chẳng hạn tránh trường hợp sau khi người dùng logout hệ thống nhưng khi nhấn nút "Back" của trình duyệt thì vẫn hiển thị các nội dung khi đăng nhập)

Với method cookie trên response object cho phép chúng ta dễ dàng đính kèm một cookie đến response. Chẳng hạn:

return response($content)
                ->header('Content-Type', $type)
                ->cookie('name', 'value', $minutes);

Method cookie này cũng nhận các tham số đặc biệt khác như setcookie của PHP.

->cookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly)

Theo mặc định thì tất cả cookie tạo bởi Laravel đều được mã hóa khi đăng ký để user không thể thay đổi và đọc được chúng.


Nếu bạn muốn tắt mã hóa một cookie nào đó trong ứng dụng, bạn có thể liệt kê nó trong $except của middleware 

App\Http\Middleware\EncryptCookies.

protected $except = [
    'name',
];

II. Chuyển hướng (Redirect)

1. Chuyển hướng đến URI (Redirecting to URI)

Redirect response khởi tạo từ lớp Illuminate\Http\RedirectResponse, chứa các header thích hợp để xây dựng một redirect response. Trong Laravel có rất nhiều cách để khởi tạo lớp RedirectResponse, nhưng có lẽ đơn giản nhất là sử dụng global helper function redirect.

Route::get('dashboard', function () {
    return redirect('home/dashboard');
});

Cú pháp quen thuộc quá  với các bạn rồi phải không nào, tham số mà method redirect nhận đó chính là URI cần đến.

Nếu thỉnh thoảng bạn muốn khi người dùng submit form mà bị lỗi thì chuyển về trang trước để người dùng kiểm tra lại. Cách giải quyết thì trong các tập trước mình đã có nói đến rồi, ở tập này mình chỉ nhắc lại. Chúng ta sẽ sử dụng method back, cùng với withInput để đáp ứng yêu cầu trên.

return back()->withInput();

2. Chuyển hướng đến route được đặt tên (Redirecting to named route)

Khi bạn gọi method redirect mà không truyền tham số vào, thì một lớp khởi tạo Illuminate\Routing\Redirector sẽ được trả về. Do đó, ta có thể gọi bất kỳ method nào từ lớp Redirector đó. Chẳng hạn để redirect đến named route, bạn chỉ cần kết nối theo phương thức route.

return redirect()->route('name_route');

Ví dụ chúng ta đăng ký hai route như sau:

route/web.php

Route::get('/', function() {
    return redirect()->route('home');
}); 

Route::get('/home', function() {
    return 'Home page';
})->name('home');

Như bạn thấy, khi mình truy cập route / thì sẽ redirect đến route có name là home.

Nếu URI route của bạn chứa tham số, bạn có thể truyền dữ liệu thông qua tham số thứ hai dưới dạng mảng. Chẳng hạn:

route/web.php

Route::get('/', function() {
    return redirect()->route('profile', ['id' => 1]);
}); 

Route::get('/profile/{id}', function($id) {
    return 'Profile '. $id;
})->name('profile');

Đây là kết quả khi truy cập đường dẫn http://localhost:8000:

Ngoài ra chúng ta cũng có thể truyền dữ liệu dưới dạng object model nếu bạn đang chuyển hướng đến URI có tham số dạng ID. Giá trị ID sẽ được xuất tự động

return redirect()->route('profile', [$user]);

Nếu bạn không muốn truyền giá trị ID theo mặc định, bạn có thể thay đổi bằng cách khai báo phương thức getRouteKey ở trong file model để thay đổi key ID mặc định.

public function getRouteKey()
{
    return $this->slug;
}

Hiện giờ do chúng ta chưa tìm hiểu về "Eloquent ORM" nên không thể test được vì vậy chỉ có thể tự kiểm chứng khi đã tìm hiểu vào các tập sau.

3. Chuyển hướng đến controller action (Redirecting to controller action)

Chúng ta có thể tạo chuyển hướng đến route chứa controller action thông qua method action. Việc của bạn là chỉ cần truyền tên controller và tên method chứa trong route muốn chuyển đến. Nhớ rằng bạn không cần phải viết đầy đủ namespace App\Http\Controllers bởi vì RouteServiceProvider đã tự động load cho bạn rồi.

Ví dụ bây giờ chúng ta đăng ký hai route sau:

route/web.php

Route::get('/', function() {
    return redirect()->action('HomeController@show');
}); 

Route::get('/home', 'HomeController@show');

Tiếp theo là tạo controller HomeController và định nghĩa method show như sau:

public function show()
{
    return 'Home page';
}

Giờ thử truy cập hãy thử truy cập route / bằng đường dẫn http://localhost:8000/ xem, chắc chắn nó sẽ chuyển hướng bạn đến route /home.

Lưu ý: Các controller action khai báo trong action yêu cầu đã được đăng ký trong route nào đó, nếu không sẽ xuất hiện lỗi. 

4. Chuyển hướng đến tên miền bên ngoài (Redirecting to external domain)

Nếu bạn muốn chuyển hướng ứng dụng đến một tên miền khác thì có thể sử dụng method away với tham số là URL đầy đủ cần đến.

return response()->away('https://facebook.com');

5. Chuyển hướng với flash session (Redirecting with flash session)

Việc Redirect đến một URL mới và flash session thường được thực hiện cùng một lúc. Chẳng hạn bây giờ chúng ta đăng ký hai route như sau:

Route::get('/', function() {
    return redirect('home')->with('name', 'Pham Nhan');
}); 

Route::get('/home', function() {
    return view('home');
});

Tại action của route /, chúng ta thực thi chuyển hướng đến URI /home, đồng thời thực hiện flash session với method with. Sau khi chuyển hướng thì flash session data sẽ được thiết lập, lúc này chúng ta chỉ cần lấy nó ra và sử dụng thôi. Bây giờ mình sẽ tạo blade view home với nội dung sau:

<h1>
    @if (session('name'))
        Welcome, {{ session('name') }}
    @else
        Welcome
    @endif
</h1>

Trước tiên chúng ta cần thực kiện câu điều kiện để kiểm tra xem có tồn tại flash session với tên là name hay không thông qua method session. Để render cái flash session name thì mình cũng sử dụng global helper session với tham số là tên của flash session cần lấy. Từ đó bạn có thể rút ra được nếu không tồn tại flash session thì method session sẽ trả về false.

Bây giờ chúng ta test xem nó đã hoạt động OK chưa. Như xử lý logic ở trên thì nếu vào đường dẫn http://localhost:8000/home thông qua redirect thì nó sẽ in ra màn hình như thế này:

Còn nếu vào URL http://localhost:8000/home trực tiếp thì ta sẽ được kết quả:

III. Các loại response khác (Other response types)

Method response có thể dùng để khởi tạo các loại response khác khi bạn không truyền bất kỳ tham số nào vào nó. Lúc này method response sẽ trả về lớp khởi tạo Illuminate\Contracts\Routing\ResponseFactory.

1. View response

Nếu bạn cần kiểm soát trạng thái của response cũng như header mà vẫn muốn trả về nội dung cho user thì bạn có thể làm như sau:

return response()
            ->view('hello', $data, 200)
            ->header('Content-Type', $type);

Còn nếu như bạn không cần thay đổi status code HTTP hay tùy chỉnh gì header thì việc chỉ khai báo method view nên được lựa chọn.

2. JSON response

Phương thức json sẽ tự động set Content-type: application/json, chuyển đổi các mảng về dạng JSON sử dụng phương thức json_encode của PHP.

return response()->json([
    'name' => 'Phàm Nhân',
    'state' => 'VN'
]);

Nếu muốn tạo JSONP response, bạn có thể sử dụng phương thức json cùng với withCallback.

return response()
            ->json(['name' => 'Abigail', 'state' => 'CA'])
            ->withCallback($request->input('callback'));

JSONP dùng để thực thị lấy dữ liệu từ máy chủ trong một tên miền khác thông qua hàm callback tự định nghĩa. Để hiểu rõ hơn các bạn có thể tìm hiểu thêm trên Google.

3. Download file

Method download thường được sử dụng để tạo một response bắt buộc người dùng download một file tại đường dẫn nhất định.

return response()->download($pahtFile, $nameFile, $headers);

Method này chấp nhận tham số thứ hai để thay đổi tên file khi người dùng download. Ngoài ra ta có thể truyền thêm mảng cấu hình các header thông qua tham số thứ ba.

Lưu ý: Mặc định method download sẽ được gán base path public cho đường dẫn fle donwload.

Ví dụ bây giờ mình muốn download file resources/views/xinchao.php 

Route::get('/download', function() {
    return response()->download('../resources/views/xinchao.php');
});

Bây giờ thử chạy route download bằng đường dẫn sau:

http://localhost:8000/download

 đó xem, ta sẽ thu được kết quả như thế này:

Nếu bạn muốn đặt 1 cái tên khác khi download, có thể thêm tham số thứ hai như sau:

Route::get('/download', function() {
    return response()->download('../resources/views/xinchao.php', 'tenkhac.php');
});

Như đã nói ở trên, chúng ta có thể truyền mảng header tại tham số thứ ba của method download.

return response()->download($pathFile, $nameFile, $headers);

Bạn có thể khai báo các header theo dạng cấu trúc mảng.

$headers = [
    'X-Header-One' => 'Header value 1',
    'X-Header-Two' => 'Header value 2',
    // ...
];
Lưu ý: Nếu muốn thêm tham số header mà không thay đổi tên file donwload thì tại tham số thứ hai khai báo giá trị null.
return response()->download($pathFile, null, $headers);

Ngoài ra bạn có thể xóa file ngay sau khi người dùng download bằng cách sử dụng method deleteFileAfterSend.

return response()->download($pathFile, $nameFile, $headers)->deleteFileAfterSend();

4. Streamed download

Nếu bạn muốn người dùng download một nội dung nào đó mà chưa được ghi vào file nào trong hệ thống của bạn thì có thể dùng method streamDownload.

Phương thức streamDownload sẽ nhận ba tham số:

  • Tham số thứ nhất là một Closure có nhiệm vụ thực hiện xử lý để echo nội dung cho file.
  • Tham số thứ hai là tên file download.
  • Tham số thứ ba (tùy chọn) là các thiết lập header.
return response()->streamDownload(Closure, $nameFile, $headers);

Bạn có thể tạo route này để test:

Route::get('/stream', function() {
    return response()->streamDownload(function() {
        echo 'Pham Nhan';
    }, 'users.txt');
});

Các bạn cứ chạy xem kết quả thì sẽ hình dung ra ngay.

Nội dung của file users.txt sau khi tải về sẽ là:

Pham Nhan
Lưu ý: Bắt buộc phải dùng lệnh echo thì nội dung mới ghi được trên file download. Nội dung của file chỉ được là dạng text.

5. File response

Nếu bạn muốn trả về response của các image, pdf... để hiển thị trên trình duyệt người dùng thay vì tải về, bạn có thể sử dụng method file. Method này chấp nhận tham số thứ nhất là path file cần hiển thị, tham số thứ hai tùy chọn chứa mảng thiết lập header response.

return response()->file($pathFile, $headers);
Lưu ý: Base path của đường dẫn file trong method file cũng là public giống như method download.

Chẳng hạn mình đã thêm một ảnh trong folder public.

Sau đó mình đăng ký một route như sau:

Route::get('/image', function() {
    return response()->file('cuccc.jpg');
});

Và đây là kết quả:

IV. Reponse macro

Chúng ta cùng tìm hiểu chút về thuật ngữ "Macro" trước các bạn nhé vì xuyên suốt series có thể ta sẽ gặp lại thuật ngữ này rất nhiều. Hiểu nôm na thì "Macro" sẽ định nghĩa một lệnh riêng lẻ, trong đó sẽ thực hiện các chuỗi lệnh nào đó để trả về kết quả, đại loại là khá giống với function.

Nếu bạn thích tự định nghĩa một loại response tùy chỉnh để sử dụng trong nhiều route và controller, bạn có thể sử dụng phương thức macro của Response facade. Bạn có thể tạo một service provider với tên ResponseMacroServiceProvider chẳng hạn, tại method boot bạn sẽ thực hiện việc định nghĩa các custom response.

Chẳng hạn mình muốn tạo một response để có thể trả về chuỗi in hoa, mình sẽ đăng ký với macro như sau:

app/Providers/ReponseMacroServiceProvider.php

use Illuminate\Support\Facades\Response;

public function boot()
{
    Response::macro('caps', function ($value) {
        return Response::make(strtoupper($value));
    });
}

Như bạn thấy chúng ta sẽ sử dụng hai method Response::macroResponse::make.

Với method Response::macro: dùng để định nghĩa một custom response

  • Tham số thứ nhất sẽ là tên của custom response.
  • Tham số thứ hai sẽ là một Closure, nhận dữ liệu truyền về qua $value.

Với method Response::make: dùng để tạo response, nó sẽ nhận tham số là các chuỗi xử lý $value trước khi được trả về.

Ok, vậy ta đã có thể sử dụng response caps như những các loại response khác.

Route::get('caps/{str}', function($str) {
    return response()->caps($str);
});

Nhớ đừng quên liệt kệ service provider vừa tạo trong config/app.php nhé.

Lời kết:

Vậy là chúng ta hoàn thành bài học về Response Laravel rồi đấy không quá khó phải không nào. Chúc các bạn học tốt!

Bình luận