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 encabezado if-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:

  1. Al establecer el nombre del archivo de descarga, el archivo será descargado en lugar de mostrarse en el navegador.
  2. 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",
        ]);
    }
}