การตอบสนอง
การตอบสนองจริงๆ แล้วเป็นอ็อบเจ็กต์ support\Response
เพื่อความสะดวกในการสร้างอ็อบเจ็กต์นี้ webman จึงได้จัดเตรียมฟังก์ชันช่วยเหลือบางอย่างให้
คืนค่าการตอบสนองใดๆ
ตัวอย่าง
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman');
}
}
ฟังก์ชัน response ถูกกำหนดไว้ดังนี้:
function response($body = '', $status = 200, $headers = array())
{
return new Response($status, $headers, $body);
}
คุณยังสามารถสร้างอ็อบเจ็กต์ response
ที่ว่างเปล่าก่อน แล้วใช้ $response->cookie()
$response->header()
$response->withHeaders()
$response->withBody()
ในตำแหน่งที่เหมาะสมเพื่อตั้งค่าหมายเลขการตอบสนอง
public function hello(Request $request)
{
// สร้างอ็อบเจ็กต์
$response = response();
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่า cookie
$response->cookie('foo', 'value');
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่า http header
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่าข้อมูลที่จะส่งคืน
$response->withBody('返回的数据');
return $response;
}
คืนค่า json
ตัวอย่าง
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}
ฟังก์ชัน json ถูกกำหนดไว้ดังนี้
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
คืนค่า xml
ตัวอย่าง
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
$xml = <<<XML
<?xml version='1.0' standalone='yes'?>
<values>
<truevalue>1</truevalue>
<falsevalue>0</falsevalue>
</values>
XML;
return xml($xml);
}
}
ฟังก์ชัน xml ถูกกำหนดไว้ดังนี้:
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
คืนค่ามุมมอง
สร้างไฟล์ app/controller/FooController.php
ดังนี้
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return view('foo/hello', ['name' => 'webman']);
}
}
สร้างไฟล์ app/view/foo/hello.html
ดังนี้
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>
การเปลี่ยนเส้นทาง
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return redirect('/user');
}
}
ฟังก์ชัน redirect ถูกกำหนดไว้ดังนี้:
function redirect($location, $status = 302, $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
การตั้งค่า header
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman', 200, [
'Content-Type' => 'application/json',
'X-Header-One' => 'Header Value'
]);
}
}
คุณยังสามารถใช้วิธีการ header
และ withHeaders
เพื่อกำหนด header ทีละอันหรือกลุ่มได้
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->header('Content-Type', 'application/json')
->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
}
}
คุณยังสามารถตั้งค่า header ล่วงหน้าและตั้งค่าสิ่งที่จะส่งคืนได้ในตอนท้าย
public function hello(Request $request)
{
// สร้างอ็อบเจ็กต์
$response = response();
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่า http header
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่าข้อมูลที่จะส่งคืน
$response->withBody('返回的数据');
return $response;
}
การตั้งค่า cookie
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->cookie('foo', 'value');
}
}
คุณยังสามารถตั้งค่า cookie ล่วงหน้าและตั้งค่าสิ่งที่จะส่งคืนได้ในตอนท้าย
public function hello(Request $request)
{
// สร้างอ็อบเจ็กต์
$response = response();
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่า cookie
$response->cookie('foo', 'value');
// .... ข้ามตรรกะทางธุรกิจ
// ตั้งค่าข้อมูลที่จะส่งคืน
$response->withBody('返回的数据');
return $response;
}
พารามิเตอร์ทั้งหมดของวิธี cookie คือ:
cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)
คืนค่าไฟล์
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->file(public_path() . '/favicon.ico');
}
}
- webman รองรับการส่งไฟล์ขนาดใหญ่
- สำหรับไฟล์ขนาดใหญ่ (มากกว่า 2M) webman จะไม่อ่านไฟล์ทั้งหมดในหน่วยความจำในครั้งเดียว แต่จะอ่านไฟล์เป็นส่วนๆ ในเวลาที่เหมาะสมและส่งออก
- webman จะปรับความเร็วในการอ่านไฟล์ให้เหมาะสมกับความเร็วในการรับของไคลเอนต์ เพื่อให้การส่งไฟล์เร็วที่สุดในขณะที่ลดการใช้งานหน่วยความจำให้น้อยที่สุด
- การส่งข้อมูลเป็นแบบไม่บล็อกจะไม่ส่งผลต่อการประมวลผลคำขออื่นๆ
- วิธี file จะเพิ่ม
if-modified-since
header โดยอัตโนมัติและตรวจสอบif-modified-since
header ในคำขอถัดไป หากไฟล์ไม่มีการเปลี่ยนแปลง จะตอบกลับ 304 โดยตรงเพื่อประหยัดแบนด์วิธ - ไฟล์ที่ส่งออกจะถูกส่งไปยังเบราว์เซอร์โดยใช้
Content-Type
header ที่เหมาะสมโดยอัตโนมัติ - หากไฟล์ไม่อยู่ จะถูกแปลงเป็นการตอบกลับ 404 โดยอัตโนมัติ
ดาวน์โหลดไฟล์
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->download(public_path() . '/favicon.ico', '文件名.ico');
}
}
วิธี download มีความคล้ายคลึงกับวิธี file ความแตกต่างคือ
1、ตั้งชื่อไฟล์ที่ดาวน์โหลดจะทำให้ไฟล์ถูกดาวน์โหลด แทนที่จะถูกแสดงในเบราว์เซอร์
2、วิธี download จะไม่ตรวจสอบ if-modified-since
header
รับข้อมูลที่ส่งออก
บางไลบรารีจะพิมพ์เนื้อหาของไฟล์ไปยังการส่งออกมาตรฐาน หมายถึงข้อมูลจะถูกพิมพ์ในเทอร์มินัลเท่านั้น ไม่ได้ส่งไปยังเบราว์เซอร์ ในกรณีนี้เราจำเป็นต้องใช้ ob_start();
ob_get_clean();
เพื่อจับข้อมูลไปยังตัวแปร แล้วส่งข้อมูลไปยังเบราว์เซอร์ เช่น
<?php
namespace app\controller;
use support\Request;
class ImageController
{
public function get(Request $request)
{
// สร้างภาพ
$im = imagecreatetruecolor(120, 20);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
// เริ่มจับข้อมูลการส่งออก
ob_start();
// ส่งภาพ
imagejpeg($im);
// รับเนื้อหาของภาพ
$image = ob_get_clean();
// ส่งภาพ
return response($image)->header('Content-Type', 'image/jpeg');
}
}
การตอบสนองแบบแบ่งส่วน
บางครั้งเราต้องการส่งคำตอบแบบแบ่งส่วนสามารถดูตัวอย่างด้านล่าง
<?php
namespace app\controller;
use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;
use Workerman\Timer;
class IndexController
{
public function index(Request $request): Response
{
//รับการเชื่อมต่อ
$connection = $request->connection;
// กำหนดเวลาในการส่ง http body
$timer = Timer::add(1, function () use ($connection, &$timer) {
static $i = 0;
if ($i++ < 10) {
// ส่ง http body
$connection->send(new Chunk($i));
} else {
// ลบตัวจับเวลาไม่จำเป็นออก เพื่อลดการเกิด memory leak
Timer::del($timer);
// ส่ง Chunk ที่ว่างเปล่าเพื่อแจ้งให้ไคลเอนต์ทราบว่าการตอบสนองเสร็จสิ้น
$connection->send(new Chunk(''));
}
});
// ส่ง http header ที่มี Transfer-Encoding: chunked ก่อน และส่ง http body แบบอะซิงโครนัส
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
หากคุณเรียกใช้โมเดลขนาดใหญ่ ให้ดูตัวอย่างด้านล่าง
composer require webman/openai
<?php
namespace app\controller;
use support\Request;
use Webman\Openai\Chat;
use Workerman\Protocols\Http\Chunk;
class ChatController
{
public function completions(Request $request)
{
$connection = $request->connection;
// https://api.openai.com ที่ไม่สามารถเข้าถึงในประเทศสามารถใช้ที่อยู่ https://api.openai-proxy.com แทนได้
$chat = new Chat(['apikey' => 'sk-xx', 'api' => 'https://api.openai.com']);
$chat->completions(
[
'model' => 'gpt-3.5-turbo',
'stream' => true,
'messages' => [['role' => 'user', 'content' => 'hello']],
], [
'stream' => function($data) use ($connection) {
// ส่งต่อข้อมูลที่มีอยู่ไปยังเบราว์เซอร์เมื่อ API ของ openai ส่งข้อมูลกลับ
$connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
},
'complete' => function($result, $response) use ($connection) {
// ตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นเมื่อตอบสนองเสร็จสิ้น
if (isset($result['error'])) {
$connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
}
// ส่ง Chunk ที่ว่างเปล่าเพื่อแสดงว่าการตอบสนองเสร็จสิ้น
$connection->send(new Chunk(''));
},
]);
// ส่ง http header ก่อน และข้อมูลในภายหลังแบบอะซิงโครนัส
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}