# 响应

响应需要返回一个 herosphp\core\HttpResponse 对象,为了方便创建这个对象,我们在框架底层做了一层封装, 可以在控制器返回任意类型的数据(数组、字符串、数字,对象...),如果返回对象的话,该对象需要实现 JsonAble 接口。

当然herosphp提供了一些助手函数。

    // response
    GF::response(int $code = 200, array $headers = [], mixed $body = ''):HttpResponse;
    // redirect
    GF::redirect(string $url, int $code = 301): HttpResponse

# 返回一个任意响应

<?php

declare(strict_types=1);
/**
 * This file is part of monda-worker.
 *
 * @contact  mondagroup_php@163.com
 */

namespace app\modules\admin\action;

use app\utils\resp\Result;
use herosphp\annotation\Controller;
use herosphp\annotation\Get;
use herosphp\core\BaseController;
use herosphp\core\Config;

#[Controller(name: KeyController::class, desc: '公钥')]
class KeyController extends BaseController
{
    #[Get(uri: '/admin/key/publicKey', desc: '获取公钥')]
    public function publicKey(): string
    {
        return  Config::get(name:'rsa', key: 'public_key');
    }
}

你也可以先创建一个空的response对象,然后在适当的位置利用$response->cookie() $response->header() $response->withHeaders() $response->withBody()设置返回内容。

public function hello(): HttpResponse
{
    // 创建一个对象
    $response = GF::response();
    // .... 业务逻辑省略
    // 设置cookie
    $response->cookie('foo', 'value');
    // .... 业务逻辑省略
    // 设置http头
    $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 herosphp\core\BaseController;
use herosphp\annotation\Controller;
use herosphp\annotation\Get;

#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello()
    {
        return $this->json(['code' => 0, 'msg' => 'ok']);
    }
}

# 返回视图

新建文件 app/controller/Foo.php 如下

<?php
namespace app\controller;

use herosphp\core\BaseController;
use herosphp\annotation\Controller;
use herosphp\annotation\Get;


#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello()
    {
        return $this->html('foo/hello', ['name' => 'herosphp']);
    }
}

新建文件 {BASE_PATH}/default/foo/hello.html 如下

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>herosphpApp</title>
  </head>
  <body>
    hello {$name}
  </body>
</html>

# 重定向

<?php
namespace app\controller;

use herosphp\GF;
use herosphp\core\BaseController;
use herosphp\annotation\Controller;
use herosphp\annotation\Get;

#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello()
    {
        return GF::redirect('/user');
    }
}

# header 设置

<?php
namespace app\controller;

use herosphp\core\HttpRequest;
use herosphp\GF;
use herosphp\core\BaseController;

#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello(HttpRequest $request)
    {
        return GF::response(200, [
            'Content-Type' => 'application/json',
            'X-Header-One' => 'Header Value'
        ],'hello herosphp');
    }
}

也可以利用headerwithHeaders方法来单个或者批量设置 header。

<?php
namespace app\controller;

use herosphp\core\HttpRequest;
use herosphp\GF;
use herosphp\core\BaseController;

#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello(HttpRequest $request)
    {
        return GF::response(body:'hello herosphp')
        ->header('Content-Type', 'application/json')
        ->withHeaders([
            'X-Header-One' => 'Header Value 1',
            'X-Header-Tow' => 'Header Value 2',
        ]);
    }
}

你也可以提前设置 header,最后设置将要返回的数据。

public function hello(HttpRequest $request)
{
    // 创建一个对象
    $response = GF::response();

    // .... 业务逻辑省略

    // 设置http头
    $response->header('Content-Type', 'application/json');
    $response->withHeaders([
                'X-Header-One' => 'Header Value 1',
                'X-Header-Tow' => 'Header Value 2',
            ]);

    // .... 业务逻辑省略

    // 设置要返回的数据
    $response->withBody('返回的数据');
    return $response;
}
<?php
namespace app\controller;

use herosphp\core\HttpRequest;
use herosphp\GF;
use herosphp\core\BaseController;
use herosphp\core\HttpRequest;

#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello(HttpRequest $request)
    {
        return response(body: 'hello herosphp')
        ->cookie('foo', 'value');
    }
}

你也可以提前设置 cookie,最后设置要返回的数据。

public function hello(HttpRequest $request)
{
    // 创建一个对象
    $response = GF::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;


#[Controller(name: Foo::class, desc: 'Foo')]
class Foo extends BaseController
{
    #[Get(uri: '/hello', desc: 'foo')]
    public function hello(HttpRequest $request)
    {
        return GF::response()->file(PUBLIC_PATH. '/favicon.ico');
    }
}

说明

  • Herosphp 支持发送超大文件
  • 对于大文件(超过 2M),workerman 不会将整个文件一次性读入内存,而是在合适的时机分段读取文件并发送
  • workerman 会根据客户端接收速度来优化文件读取发送速度,保证最快速发送文件的同时将内存占用减少到最低
  • 数据发送是非阻塞的,不会影响其它请求处理
  • file 方法会自动添加if-modified-since头并在下一个请求时检测if-modified-since头,如果文件未修改则直接返回 304 以便节省带宽
  • 发送的文件会自动使用合适的Content-Type头发送给浏览器
  • 如果文件不存在,会自动转为 404 响应

# 下载文件

<?php
namespace app\controller;

use herosphp\core\HttpRequest as Request;

class Foo
{
    public function hello(Request $request)
    {
        return GF::response()->download(PUBLIC_PATH . '/favicon.ico', '可选的文件名.ico');
    }
}

download 方法与 file 方法的区别是 download 方法一般用于下载并保存文件,并且可以设置下载的文件名。download 方法不会检查if-modified-since头。其它与 file 方法行为一致。

# 获取输出

有些类库是将文件内容直接打印到标准输出的,也就是数据会打印在命令行终端里,并不会发送给浏览器,这时候我们需要通过ob_start(); ob_get_clean(); 将数据捕获到一个变量中,再将数据发送给浏览器,例如:

<?php

namespace app\controller;

use herosphp\core\HttpRequest as Request;

class Image
{
    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 GF::response(body: $image)->header('Content-Type', 'image/jpeg');
    }
}
上次更新: 10/27/2022, 11:18:25 AM