当前位置:   article > 正文

hyperf 中 http 设置响应头为 text/event-stream_hyperf 设置响应头

hyperf 设置响应头

在hyperf中 设置响应头让后端直接给前端返回 event-stream 流的需求,发现hyperf 中在重写 SwooleConnection 时,设置的属性 $response 为 protected ,不能直接调用此属性。

 

为了让我能设置此响应头,我 继承了 Hyperf\HttpServer\Server 类 并重写了  onRequest 方法,

具体的做法如下:

首先创建StreamServer 类, 并重写了onRequest 方法,如果只是想某几个路由 重写,需要自己写一下配置

  1. <?php
  2. namespace App\Service\Server;
  3. use Hyperf\Dispatcher\HttpDispatcher;
  4. use Hyperf\ExceptionHandler\ExceptionHandlerDispatcher;
  5. use Hyperf\HttpServer\ResponseEmitter;
  6. use Hyperf\HttpServer\Server;
  7. use Psr\Container\ContainerInterface;
  8. use Swoole\Http\Request;
  9. use Swoole\Http\Response;
  10. class StreamServer extends Server
  11. {
  12. const STREAM_URL = [
  13. '/chat/chat/test'
  14. ];
  15. public function __construct(ContainerInterface $container, HttpDispatcher $dispatcher, ExceptionHandlerDispatcher $exceptionHandlerDispatcher, ResponseEmitter $responseEmitter)
  16. {
  17. parent::__construct($container, $dispatcher, $exceptionHandlerDispatcher, $responseEmitter);
  18. }
  19. /**
  20. * @param Request $request
  21. * @param Response $response
  22. */
  23. public function onRequest($request, $response): void
  24. {
  25. $pathInfo = $request->server['path_info'];
  26. if (in_array($pathInfo, self::STREAM_URL)) {
  27. $response->header('Content-Type', 'text/event-stream');
  28. $response->header('Cache-Control', 'no-cache');
  29. $response->header('Connection', 'keep-alive');
  30. }
  31. parent::onRequest($request, $response);
  32. }
  33. }

然后再将 config/autoload/server.php 中的 http 配置 修改,如下图

这样,我们在控制器里面就可以实现 response 的header 更改为 text/event-stream。

  1. <?php
  2. use Hyperf\HttpServer\Contract\ResponseInterface;
  3. $wrResponse = ApplicationContext::getContainer()->get(ResponseInterface::class);
  4. //此处可以将返回的数据 直接打印字屏幕上
  5. $wrResponse->write("data: " . $res . "\n\n");

 也就实现了 chatgpt 的打印流的关键步骤,下面贴出部分代码,使用guzzlehttp 处理chatgpt 返回的流式数据

  1. use GuzzleHttp\Client;
  2. use GuzzleHttp\Psr7\Utils;
  3. use Hyperf\Guzzle\ClientFactory;
  4. use Hyperf\HttpServer\Contract\ResponseInterface;
  5. use Hyperf\Utils\ApplicationContext;
  6. use Hyperf\Context\Context;
  7. class HttpRequestService
  8. {
  9. private $clientFactory;
  10. //此处可以模拟类属性,获取协程中的属性
  11. public function __get($key)
  12. {
  13. $className = static::class;
  14. $key = env('APP_NAME').$className . $key;
  15. return Context::get($key);
  16. }
  17. //此处可以模拟类属性,将属性存在协程中
  18. public function __set($key, $value)
  19. {
  20. $className = static::class;
  21. $key = env('APP_NAME').$className . $key;
  22. Context::set($key, $value);
  23. }
  24. public function __construct()
  25. {
  26. $this->clientFactory = make(ClientFactory::class);
  27. }
  28. /**
  29. * 是否使用流式获取
  30. * @param bool $stream
  31. * @return $this
  32. */
  33. private function setStream($stream = false)
  34. {
  35. $this->stream = $stream;
  36. return $this;
  37. }
  38. /**
  39. * 初始化
  40. * @param string $domainUrl
  41. * @param array $header
  42. * @param int $timeout
  43. */
  44. private function init($domainUrl = '', $header = [], $timeout = 120)
  45. {
  46. $config = ['timeout' => $timeout];
  47. if (!empty($domainUrl)) {
  48. $config['base_uri'] = $domainUrl;
  49. }
  50. if ($this->stream) {
  51. $config['stream'] = true;
  52. }
  53. if (!empty($header) && is_array($header)) {
  54. $config = array_merge($config, ['headers' => $header]);
  55. }
  56. /** @var Client $client */
  57. $this->client = $this->clientFactory->create($config);
  58. }
  59. public function postStream($domain, $uri, $params, $header = [])
  60. {
  61. $this->setStream(true);
  62. $this->init($domain, $header);
  63. $type = 'json';
  64. $requestParmas = [$type => $params];
  65. $response = $this->client->post($uri, $requestParmas);
  66. $responseData = $response->getBody(); //获取流数据
  67. $parseData = [];
  68. $wrResponse = ApplicationContext::getContainer()->get(ResponseInterface::class);
  69. while (!$responseData->eof()) {
  70. $res = trim(Utils::readLine($responseData)); //一行一行读取
  71. $res = str_replace("data: ", "", $res);
  72. if (empty($res)) {
  73. continue;
  74. }
  75. $wrResponse->write("data: " . $res . "\n\n");
  76. if ($res == '[DONE]') {
  77. break;
  78. }
  79. //休眠时间,用于前端展示
  80. usleep(100000);
  81. $res = json_decode($res, true);
  82. if (!empty($res)) {
  83. $parseData[] = $res['choices'][0]['delta']['content'] ?? ($res['choices'][0]['delta']['role'] ?? '');
  84. }
  85. }
  86. return $parseData;
  87. }
  88. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/335515
推荐阅读
相关标签
  

闽ICP备14008679号