Skip to content

Instantly share code, notes, and snippets.

@jenky
Created May 7, 2019 05:30
Show Gist options
  • Save jenky/0a3862ab8004327894a14680ba83158a to your computer and use it in GitHub Desktop.
Save jenky/0a3862ab8004327894a14680ba83158a to your computer and use it in GitHub Desktop.
Laravel idempotency middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
class Idempotency
{
/**
* The header name.
*
* @var string
*/
const IDEMPOTENCY_HEADER = 'Idempotency-Key';
/**
* The idempotency cache time frame.
*
* @var int
*/
const EXPIRATION_IN_MINUTES = 1440; // 24 hours
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param int $retryAfter
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next, $retryAfter = self::EXPIRATION_IN_MINUTES)
{
$key = $request->header(static::IDEMPOTENCY_HEADER);
if ($request->isMethodIdempotent() || ! $key) {
return $next($request);
}
$cacheKey = 'Idempotency::'.$key;
$uri = str_replace($request->root(), '', $request->fullUrl()) ?: '/';
if (Cache::has($cacheKey)) {
extract(Cache::get($cacheKey));
if ($path == $uri) {
$headers[static::IDEMPOTENCY_HEADER] = $key;
return response($content, $status, $headers);
}
}
$response = $next($request);
if ($response->isClientError()) {
// Don't idempotent client error request.
return $response;
}
$response->header(static::IDEMPOTENCY_HEADER, $key);
Cache::put($cacheKey, [
'path' => $uri,
'content' => $response->content(),
'status' => $response->status(),
'headers' => $this->headers($response->headers->all()),
], now()->addMinutes($retryAfter));
return $response;
}
/**
* Format the headers before saving to cache storage.
*
* @param array $headers
* @return array
*/
protected function headers(array $headers)
{
return Arr::only($headers, [
'content-type',
]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment