365Werk/
etagconditionals
g4t.io

Laravel ETag and Conditionals Package

by Paul Redmond

Laravel ETag and Conditionals is a package that provides a set of middleware for ETags and HTTP conditional requests.

README from 365Werk/etagconditionals Open on GitHub →

Laravel ETag & Conditionals

Latest Version on Packagist Total Downloads StyleCI Scrutinizer Code Quality Build Status

This package provides a set of middlewares to both set ETags and handle HTTP Conditional Requests.

Currently both If-None-Matched and If-Match are supported.

The package aims to provide the tools to provide better client-side caching when building an API with Laravel, as well as preventing mid-air collisions.

When using the package and enabling the middleware, your client (browser) will take care of handling the caching provided by the ETag and If-None-Match headers automatically.

Installation

Via Composer


$ composer require werk365/etagconditionals

Usage

You can either use the middleware group, automatically applying all available middleware (recommended if using an apiResource route for example), by setting the etag middleware, or apply the middlewares individually.

Currently available middleware:

  • setEtag
  • ifMatch
  • ifNoneMatch

setEtag

Method: Any

This middleware will set the ETag header on your responses. The ETag header is equal to a md5 hash of $response->getContent(). HEAD requests are supported by transforming the request to a GET request and changing it back on the response.

ifMatch

Method: PATCH

This middleware will create a new request to the GET equivalent of the endpoint called and retrieve the current content. After this, a hash of the current content and the If-Match hash will be compared. If the hashes match, the PATCH request will be allowed through the middleware, but if there is no match, 412 will be returned.

Important Since the internal GET request created will also pass through enabled middleware, you might run in to some cases where this is causing issues. For example: if you have a middleware that changes the response body that was not applied to the response that the If-Match etag belongs to, this will result in non-matching hashes.

For this scenario, this middleware sets a X-From-Middleware: IfMatch header which you can use in other middleware to filter these requests. Please note that since this header could also be set by a client, it should never be used to skip anything important like auth middleware.

ifNoneMatch

Method: GET|HEAD

This middleware will simply compare the submitted If-None-Match header to a newly created etag of the response. If there is no match, 200 is returned, with the new response in the case of a GET request. If the hashes are matching, 304 is returned with no content, allowing the browser to used cached content instead.

Comparison algorithms

By default, a weak comparison algorithm will be used for both the IfMatch and IfNoneMatch ETags. In practise this means that we simply strip any W/ tags from the ETag, so they can be compared to normal tags created in the middleware. This is to support cases where certain configurations automatically add the W/ tag to our supplied ETag.

This behaviour can be changed by either publishing the config file:

$ php artisan vendor:publish --provider="Werk365\EtagConditionals\EtagConditionalsServiceProvider"

And then changing the following values:

return [
    'if_match_weak' => env('IF_MATCH_WEAK', true),
    'if_none_match_weak' => env('IF_NONE_MATCH_WEAK', true),
];

Or by setting the ENV values above.

Defining custom ETags

The static method EtagConditionals::etagGenerateUsing() allows you to have full control over how your Etag is generated by passing a callback as argument. This means you can do simple things like returning an ETag using a different algorithm, or other custom solutions.

        EtagConditionals::etagGenerateUsing(function (\Symfony\Component\HttpFoundation\Response $response) {
            return hash('sha256', $response->getContent());
        });
        EtagConditionals::etagGenerateUsing(function (\Illuminate\Http\Request $request, \Symfony\Component\HttpFoundation\Response $response) {
            return Cache::rememberForever('etag.'.$request->url(), function () use ($response) {
                 return md5($response->getContent());
            });
        });

Change log

Please see the changelog for more information on what has changed recently.

Contributing

Feel free to create issues and submit pull requests. For any PR submitted, make sure it is covered by tests or include new tests.

Security

If you discover any security related issues, please email author email instead of using the issue tracker.

Credits

License

Please see the license file for more information.

More from the ecosystem

marcreichel/
laya-php
g4t.io
3 53

LayaPHP: Self-Hosted Text Classification for PHP and Laravel

Classify text in PHP without an LLM bill: typed decisions in 100+ languages, self-hosted. Laravel-ready SDK for Laya, a Jev AI alternative.

ai classification decision-engine
marcreichel/laya-php via Laravel News
Josh-Dovey/
postcodes-laravel
g4t.io
5 9

Postcodes for Laravel: GB Postcode Lookup and Geography Data

Postcodes for Laravel adds typed GB postcode lookups, validation, geography data, distance searches, and test fakes through the GB Postcodes API.

Josh-Dovey/postcodes-laravel via Laravel News
RedberryProducts/
mailbox-for-laravel
g4t.io
4 115

Mailbox for Laravel: Preview and Test Rendered Email

Mailbox for Laravel captures outgoing mail in a local dashboard and lets you test rendered HTML, recipients, and attachments with fluent assertions.

RedberryProducts/mailbox-for-laravel via Laravel News