Skip to content

Add RemoveBodyMiddleware #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## 1.0.1 under development

- no changes in this release.
- New #10: Add `RemoveBodyMiddleware` (@vjik)

## 1.0.0 June 04, 2025

- Initial release.
- Initial release.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ HTTP features:
- [`HeadRequestMiddleware`](docs/guide/en/head-request-middleware.md) — removes body from response for `HEAD` request;
- [`HttpCacheMiddleware`](docs/guide/en/http-cache-middleware.md) — implements HTTP caching using `Cache-Control`,
`ETag`, and `Last-Modified` headers;
- [`RemoveBodyMiddleware`](docs/guide/en/remove-body-middleware.md) — removes body from response by status code;
- [`TagRequestMiddleware`](docs/guide/en/tag-request-middleware.md) — adds specific header to request, which can be used
for logging or debugging purposes.

Expand Down
1 change: 1 addition & 0 deletions docs/guide/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ HTTP features. All middleware implements independent functionality and doesn't i
- [`ForceSecureConnectionMiddleware`](force-secure-connection-middleware.md)
- [`HeadRequestMiddleware`](head-request-middleware.md)
- [`HttpCacheMiddleware`](http-cache-middleware.md)
- [`RemoveBodyMiddleware`](remove-body-middleware.md)
- [`TagRequestMiddleware`](tag-request-middleware.md)

3 changes: 2 additions & 1 deletion docs/guide/en/head-request-middleware.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# `HeadRequestMiddleware`

This middleware ensures that HTTP `HEAD` requests return a response without a body, as required by the HTTP specification.
This middleware ensures that HTTP `HEAD` requests return a response without a body, as required by the HTTP
specification.

General usage:

Expand Down
43 changes: 43 additions & 0 deletions docs/guide/en/remove-body-middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# `RemoveBodyMiddleware`

This middleware removes the body from the response based on the status code. It is useful when you want to ensure that
no body content is sent for certain HTTP responses, such as `204 No Content` or `304 Not Modified`.

General usage:

```php
use Psr\Http\Message\StreamFactoryInterface;
use Yiisoft\HttpMiddleware\RemoveBodyMiddleware;

/**
* @var StreamFactoryInterface $streamFactory
*/

$middleware = new RemoveBodyMiddleware($streamFactory);
```

## Constructor parameters

### `$streamFactory` (required)

Type: `Psr\Http\Message\StreamFactoryInterface`

A PSR-17 stream factory used to create an empty body.

### `$statusCodes`

Type: `list<int>`

Default:
```php
[
100, // Continue
101, // Switching Protocols
102, // Processing
204, // No Content
205, // Reset Content
304, // Not Modified
]
```

An array of HTTP status codes for which the body should be removed.
59 changes: 59 additions & 0 deletions src/RemoveBodyMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace Yiisoft\HttpMiddleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

use function in_array;

/**
* Removes the body from the response for specific HTTP status codes.
*/
final class RemoveBodyMiddleware implements MiddlewareInterface
{
/**
* @param StreamFactoryInterface $streamFactory Factory to create a stream.
* @param array $statusCodes List of HTTP status codes for which the body should be removed.
*
* @psalm-param list<int> $statusCodes
*/
public function __construct(
private readonly StreamFactoryInterface $streamFactory,
private readonly array $statusCodes = [
100, // Continue
101, // Switching Protocols
102, // Processing
204, // No Content
205, // Reset Content
304, // Not Modified
],
) {
}

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);

return $this->shouldRemoveBody($response)
? $this->removeBody($response)
: $response;
}

private function shouldRemoveBody(ResponseInterface $response): bool
{
return in_array($response->getStatusCode(), $this->statusCodes, true);
}

private function removeBody(ResponseInterface $response): ResponseInterface
{
return $response->withBody(
$this->streamFactory->createStream(),
);
}
}
60 changes: 60 additions & 0 deletions tests/RemoveBodyMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

use HttpSoft\Message\Response;
use HttpSoft\Message\ServerRequest;
use HttpSoft\Message\StreamFactory;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Yiisoft\HttpMiddleware\RemoveBodyMiddleware;
use Yiisoft\HttpMiddleware\Tests\Support\FakeRequestHandler;

use function PHPUnit\Framework\assertSame;

final class RemoveBodyMiddlewareTest extends TestCase
{
#[TestWith([true, 200])]
#[TestWith([true, 500])]
#[TestWith([false, 100])]
#[TestWith([false, 101])]
#[TestWith([false, 102])]
#[TestWith([false, 204])]
#[TestWith([false, 205])]
#[TestWith([false, 304])]
public function testBase(bool $expectBody, int $statusCode): void
{
$streamFactory = new StreamFactory();
$requestHandler = new FakeRequestHandler(
new Response(
statusCode: $statusCode,
body: (new StreamFactory())->createStream('test')
),
);
$middleware = new RemoveBodyMiddleware($streamFactory);

$response = $middleware->process(new ServerRequest(), $requestHandler);

assertSame($expectBody ? 'test' : '', (string) $response->getBody());
}

#[TestWith([true, 200])]
#[TestWith([true, 304])]
#[TestWith([false, 100])]
#[TestWith([false, 404])]
public function testCustomStatus(bool $expectBody, int $statusCode): void
{
$streamFactory = new StreamFactory();
$requestHandler = new FakeRequestHandler(
new Response(
statusCode: $statusCode,
body: (new StreamFactory())->createStream('test')
),
);
$middleware = new RemoveBodyMiddleware($streamFactory, [100, 404]);

$response = $middleware->process(new ServerRequest(), $requestHandler);

assertSame($expectBody ? 'test' : '', (string) $response->getBody());
}
}