Resposta
A resposta é, na verdade, um objeto support\Response
. Para facilitar a criação desse objeto, o Webman fornece algumas funções auxiliares.
Retornar uma resposta arbitrária
Exemplo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman');
}
}
A função response é implementada da seguinte forma:
function response($body = '', $status = 200, $headers = array())
{
return new Response($status, $headers, $body);
}
Você também pode criar primeiro um objeto response
vazio e, em seguida, utilizar $response->cookie()
$response->header()
$response->withHeaders()
$response->withBody()
para definir o conteúdo da resposta em locais apropriados.
public function hello(Request $request)
{
// Criar um objeto
$response = response();
// .... lógica do negócio omitida
// Definir cookie
$response->cookie('foo', 'value');
// .... lógica do negócio omitida
// Definir cabeçalho http
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... lógica do negócio omitida
// Definir os dados a serem retornados
$response->withBody('返回的数据');
return $response;
}
Retornar json
Exemplo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}
A função json é implementada da seguinte forma:
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
Retornar xml
Exemplo
<?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);
}
}
A função xml é implementada da seguinte forma:
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
Retornar uma visão
Crie um arquivo app/controller/FooController.php
da seguinte forma:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return view('foo/hello', ['name' => 'webman']);
}
}
Crie um arquivo app/view/foo/hello.html
da seguinte forma:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>
Redirecionamento
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return redirect('/user');
}
}
A função redirect é implementada da seguinte forma:
function redirect($location, $status = 302, $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
Configuração de cabeçalhos
<?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'
]);
}
}
Você também pode usar os métodos header
e withHeaders
para definir cabeçalhos individualmente ou em lote.
<?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',
]);
}
}
Você também pode definir os cabeçalhos antecipadamente e, em seguida, definir os dados a serem retornados por último.
public function hello(Request $request)
{
// Criar um objeto
$response = response();
// .... lógica do negócio omitida
// Definir cabeçalho http
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... lógica do negócio omitida
// Definir os dados a serem retornados
$response->withBody('返回的数据');
return $response;
}
Configurar cookie
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->cookie('foo', 'value');
}
}
Você também pode definir o cookie antecipadamente e, em seguida, definir os dados que serão retornados por último.
public function hello(Request $request)
{
// Criar um objeto
$response = response();
// .... lógica do negócio omitida
// Definir cookie
$response->cookie('foo', 'value');
// .... lógica do negócio omitida
// Definir os dados a serem retornados
$response->withBody('返回的数据');
return $response;
}
Os parâmetros completos do método cookie são:
cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)
Retornar fluxo de arquivo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->file(public_path() . '/favicon.ico');
}
}
- O Webman suporta enviar arquivos muito grandes
- Para arquivos grandes (acima de 2M), o Webman não lerá todo o arquivo de uma só vez na memória, mas lerá e enviará o arquivo em partes no momento apropriado
- O Webman otimizará a velocidade de leitura e envio de arquivos com base na velocidade de recebimento do cliente, garantindo o envio mais rápido possível do arquivo enquanto reduz a ocupação de memória ao mínimo
- O envio de dados é não bloqueante e não afetará o processamento de outras solicitações
- O método file adicionará automaticamente o cabeçalho
if-modified-since
e verificará o cabeçalhoif-modified-since
na próxima solicitação. Se o arquivo não tiver sido modificado, retornará 304 diretamente para economizar largura de banda - O arquivo enviado usará automaticamente o cabeçalho
Content-Type
apropriado ao ser enviado para o navegador - Se o arquivo não existir, retornará automaticamente uma resposta 404
Baixar arquivo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->download(public_path() . '/favicon.ico', '文件名.ico');
}
}
O método download é basicamente semelhante ao método file, com a diferença de que
- Quando um nome de arquivo para download é definido, o arquivo será baixado, em vez de ser exibido no navegador
- O método download não verificará o cabeçalho
if-modified-since
Obter saída
Algumas bibliotecas imprimem o conteúdo do arquivo diretamente na saída padrão, o que significa que os dados serão impressos no terminal de linha de comando e não serão enviados para o navegador. Nesse caso, precisamos capturar os dados em uma variável usando ob_start();
ob_get_clean();
e, em seguida, enviar os dados para o navegador, por exemplo:
<?php
namespace app\controller;
use support\Request;
class ImageController
{
public function get(Request $request)
{
// Criar imagem
$im = imagecreatetruecolor(120, 20);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
// Começar a capturar saída
ob_start();
// Enviar imagem
imagejpeg($im);
// Obter conteúdo da imagem
$image = ob_get_clean();
// Enviar imagem
return response($image)->header('Content-Type', 'image/jpeg');
}
}
Resposta em partes
Às vezes precisamos enviar a resposta em partes, você pode se referir ao exemplo abaixo.
<?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
{
// Obter conexão
$connection = $request->connection;
// Enviar corpo http em intervalos
$timer = Timer::add(1, function () use ($connection, &$timer) {
static $i = 0;
if ($i++ < 10) {
// Enviar corpo http
$connection->send(new Chunk($i));
} else {
// Remover o temporizador desnecessário para evitar vazamento de memória com muitos temporizadores
Timer::del($timer);
// Enviar um pacote Chunk vazio para notificar o cliente que a resposta terminou
$connection->send(new Chunk(''));
}
});
// Primeiramente, enviar um cabeçalho http com Transfer-Encoding: chunked, o corpo http será enviado de forma assíncrona
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
Se você estiver chamando um modelo grande, veja o exemplo abaixo.
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 pode não ser acessível na China, você pode usar https://api.openai-proxy.com como alternativa
$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) {
// Quando a interface openai retornar dados, encaminhar para o navegador
$connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
},
'complete' => function($result, $response) use ($connection) {
// No final da resposta, verificar se há erros
if (isset($result['error'])) {
$connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
}
// Retornar um chunk vazio representa o fim da resposta
$connection->send(new Chunk(''));
},
]);
// Primeiro retornar um cabeçalho http, os dados seguintes serão retornados de forma assíncrona
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}