Respuesta
La respuesta es en realidad un objeto support\Response
, y para facilitar la creación de este objeto, Webman proporciona algunas funciones de asistente.
Devolver una respuesta arbitraria
Ejemplo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman');
}
}
La función response se implementa de la siguiente manera:
function response($body = '', $status = 200, $headers = array())
{
return new Response($status, $headers, $body);
}
También puedes crear primero un objeto response
vacío y luego, en el momento apropiado, utilizar $response->cookie()
$response->header()
$response->withHeaders()
$response->withBody()
para establecer el contenido de la respuesta.
public function hello(Request $request)
{
// Crear un objeto
$response = response();
// .... Lógica de negocio omitida
// Establecer cookie
$response->cookie('foo', 'value');
// .... Lógica de negocio omitida
// Establecer encabezado http
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... Lógica de negocio omitida
// Establecer los datos a devolver
$response->withBody('返回的数据');
return $response;
}
Devolver json
Ejemplo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}
La función json se implementa de la siguiente manera:
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
Devolver xml
Ejemplo
<?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);
}
}
La función xml se implementa de la siguiente manera:
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
Devolver vista
Crea un archivo app/controller/FooController.php
con el siguiente contenido:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return view('foo/hello', ['name' => 'webman']);
}
}
Crea un archivo app/view/foo/hello.html
con el siguiente contenido:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>
Redirección
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return redirect('/user');
}
}
La función redirect se implementa de la siguiente manera:
function redirect($location, $status = 302, $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
Configuración de encabezados
<?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'
]);
}
}
También puedes utilizar los métodos header
y withHeaders
para establecer encabezados de forma individual o en grupo.
<?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',
]);
}
}
También puedes establecer encabezados por adelantado y luego establecer los datos que se devolverán al final.
public function hello(Request $request)
{
// Crear un objeto
$response = response();
// .... Lógica de negocio omitida
// Establecer encabezado http
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... Lógica de negocio omitida
// Establecer los datos a devolver
$response->withBody('返回的数据');
return $response;
}
Establecer cookie
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->cookie('foo', 'value');
}
}
También puedes establecer cookies por adelantado y luego establecer los datos a devolver.
public function hello(Request $request)
{
// Crear un objeto
$response = response();
// .... Lógica de negocio omitida
// Establecer cookie
$response->cookie('foo', 'value');
// .... Lógica de negocio omitida
// Establecer los datos a devolver
$response->withBody('返回的数据');
return $response;
}
Los parámetros completos del método cookie son:
cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)
Devolver flujo de archivos
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->file(public_path() . '/favicon.ico');
}
}
- Webman soporta el envío de archivos muy grandes.
- Para archivos grandes (más de 2M), Webman no leerá todo el archivo en memoria de una sola vez, sino que leerá el archivo en segmentos y lo enviará en el momento adecuado.
- Webman optimizará la velocidad de lectura y envío de archivos según la velocidad de recepción del cliente, garantizando el envío de archivos de la forma más rápida posible mientras reduce al mínimo el uso de memoria.
- El envío de datos es no bloqueante y no afectará el procesamiento de otras solicitudes.
- El método file añadirá automáticamente el encabezado
if-modified-since
y en la siguiente solicitud verificará el encabezadoif-modified-since
. Si el archivo no ha cambiado, devolverá directamente un 304 para ahorrar ancho de banda. - Los archivos enviados utilizarán automáticamente el encabezado
Content-Type
adecuado al ser enviados al navegador. - Si el archivo no existe, se convertirá automáticamente en una respuesta 404.
Descargar archivos
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->download(public_path() . '/favicon.ico', '文件名.ico');
}
}
El método download es prácticamente igual al método file, con la diferencia de que:
- Al establecer el nombre del archivo de descarga, el archivo será descargado en lugar de mostrarse en el navegador.
- El método download no verificará el encabezado
if-modified-since
.
Obtener salida
Algunas bibliotecas imprimen el contenido del archivo directamente en la salida estándar, lo que significa que los datos se imprimirán en la terminal de línea de comandos y no se enviarán al navegador. En este caso, necesitamos capturar los datos en una variable mediante ob_start();
ob_get_clean();
, y luego enviar los datos al navegador, por ejemplo:
<?php
namespace app\controller;
use support\Request;
class ImageController
{
public function get(Request $request)
{
// Crear imagen
$im = imagecreatetruecolor(120, 20);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
// Comenzar a capturar la salida
ob_start();
// Salida de la imagen
imagejpeg($im);
// Obtener contenido de la imagen
$image = ob_get_clean();
// Enviar la imagen
return response($image)->header('Content-Type', 'image/jpeg');
}
}
Respuesta por partes
A veces queremos enviar la respuesta en partes, como se muestra en el siguiente ejemplo.
<?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
{
// Obtener conexión
$connection = $request->connection;
// Enviar paquetes http de forma programada
$timer = Timer::add(1, function () use ($connection, &$timer) {
static $i = 0;
if ($i++ < 10) {
// Enviar paquete http
$connection->send(new Chunk($i));
} else {
// Eliminar temporizador no utilizado para evitar fugas de memoria
Timer::del($timer);
// Enviar un paquete vacío de Chunk para notificar al cliente que la respuesta ha terminado
$connection->send(new Chunk(''));
}
});
// Primero enviar un encabezado http con Transfer-Encoding: chunked, y el cuerpo http se enviará de manera asíncrona
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
Si estás utilizando un modelo grande, consulta el siguiente ejemplo.
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;
// Si no se puede acceder a https://api.openai.com, se puede usar 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) {
// Cuando la interfaz de openai retorna datos, se envían al navegador
$connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
},
'complete' => function($result, $response) use ($connection) {
// Al finalizar la respuesta, verificar si hay errores
if (isset($result['error'])) {
$connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
}
// Enviar un chunk vacío representa el final de la respuesta
$connection->send(new Chunk(''));
},
]);
// Primero devolver un encabezado http, los datos posteriores se devolverán de forma asíncrona
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}