werk365 / etagconditionals
Laravel package to enable support for ETags and handling If-Match and If-None-Match conditional requests
Installs: 236 672
Dependents: 2
Suggesters: 0
Security: 0
Stars: 84
Watchers: 4
Forks: 9
Open Issues: 1
Requires
- illuminate/support: ~7|~8|~9|~10
Requires (Dev)
- orchestra/testbench: ~5|~6|~7|~8
- phpunit/phpunit: ~8.0|~9.0
This package is auto-updated.
Last update: 2024-10-22 13:56:34 UTC
README
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 theIf-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.