Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Standardized HTTP Response Handling in Hyperf Applications

Tech May 16 1

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...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

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...

Leave a Comment

Anonymous

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