Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Standardized HTTP Response Handling in Hyperf Applications

Tech May 16 46

This article presents two robust approaches for implementing consistent request-response formatting across Hyperf-based services. Both aim to decouple response logic from business controllers while supporting debug visibility, status code flexibility, and standardized data envelopes.

Approach One: Dependency-Injected Trait

A lightweight, controller-bound solution using constructor injection via annotations. Ideal for simple APIs where response headers are largely static.

namespace App\Support;

use App\Support\HttpCode;
use App\Support\HttpMessage;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\Di\Annotation\Inject;
use Throwable;

trait StandardResponse
{
    #[Inject]
    protected RequestInterface $request;

    #[Inject]
    protected ResponseInterface $response;

    public function ok(mixed $payload = null, string $message = HttpMessage::OK): \Psr\Http\Message\ResponseInterface
    {
        return $this->response->json([
            'code' => HttpCode::SUCCESS,
            'message' => $message,
            'data' => $payload
        ]);
    }

    public function error(Throwable $exception, int $statusCode = 200): \Psr\Http\Message\ResponseInterface
    {
        $isDebug = $this->request->input('debug') === $_ENV['APP_DEBUG'] || $_ENV['APP_DEBUG'] === 'true';

        $content = [
            'code' => HttpCode::ERROR,
            'message' => $exception->getMessage(),
            'data' => $isDebug ? $exception->getTraceAsString() : null
        ];

        return $this->response->json($content, $statusCode);
    }

    public function unauthorized(Throwable $exception): \Psr\Http\Message\ResponseInterface
    {
        $isDebug = $this->request->input('debug') === $_ENV['APP_DEBUG'] || $_ENV['APP_DEBUG'] === 'true';

        $content = [
            'code' => HttpCode::UNAUTHORIZED,
            'message' => $exception->getMessage(),
            'data' => $isDebug ? $exception->getTraceAsString() : $exception->getMessage()
        ];

        return $this->response->json($content, 401);
    }
}

Approach Two: Static Container-Driven Utility

A more flexible, framework-agnostic pattern that abstracts response construction behind static helpers. Anables full control over status codes, headers, and serialization options without requiring dependency injection in every controller.

namespace App\Support;

use App\Support\HttpCode;
use App\Support\HttpMessage;
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;

trait StandardResponse
{
    public function ok(mixed $payload = null, string $message = HttpMessage::OK, int $status = 200): \Psr\Http\Message\ResponseInterface
    {
        return self::buildResponse([
            'code' => HttpCode::SUCCESS,
            'message' => $message,
            'data' => $payload
        ], $status);
    }

    public function error(Throwable $exception, int $status = 400): \Psr\Http\Message\ResponseInterface
    {
        $isDebug = self::getRequest()->input('debug') === $_ENV['APP_DEBUG'] || $_ENV['APP_DEBUG'] === 'true';

        $content = [
            'code' => HttpCode::ERROR,
            'message' => $exception->getMessage(),
            'data' => $isDebug ? $exception->getTraceAsString() : null
        ];

        return self::buildResponse($content, $status);
    }

    public function forbidden(Throwable $exception): \Psr\Http\Message\ResponseInterface
    {
        return self::error($exception, 403);
    }

    private static function buildResponse(mixed $data, int $status = 200): \Psr\Http\Message\ResponseInterface
    {
        $response = self::container()->get(ResponseInterface::class);
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        return $response
            ->withStatus($status)
            ->withAddedHeader('Content-Type', 'application/json; charset=utf-8')
            ->withBody(new SwooleStream($json));
    }

    private static function getRequest(): RequestInterface
    {
        return self::container()->get(RequestInterface::class);
    }

    private static function container(): \Psr\Container\ContainerInterface
    {
        return ApplicationContext::getContainer();
    }
}

Both implementations support environment-aware debugging output and align with Hyperf’s PSR-7/PSR-15 architecture. The first is suitable for rapid prototyping or small-scale services; the second better suits large applications requiring centralized response orchestration and middleware compatibility.

Tags: Hyperf

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.