Selective caching


#1

I am a new user of the Slim Framework.
Want to take the occasion to thank for a ‘right sized’ framework.

Even though I am starting to have quite the hang of it, there is an issue I don’t get resolved.
I am serving a number of files pseudo-static. That means : just checking if the user has access to it and then serving through Slim.

Those files I want to have cached by the client. And that’s where I got stuck.
I found session_cache_limiter('private_no_expire'); (before session_start()) does the job decently, but it caches at once all pages, which I don’t want. How can I in the logic of my route define that certain files I serve must be cached ?

Thanks,

Camiel B.


#2

It sounds like you should add a middleware to the routes that should check authorization and then set the cache headers on the response.

The middleware might look something like this.

<?php

namespace App\Middleware;

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Router;

class AuthorizeAndCacheMiddleware
{
    protected $router;

    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    /**
     * Authentication middleware invokable class
     *
     * @param  \Psr\Http\Message\ServerRequestInterface $request  PSR7 request
     * @param  \Psr\Http\Message\ResponseInterface      $response PSR7 response
     * @param  callable                                 $next     Next middleware
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function __invoke(Request $request, Response $response, $next)
    {
        if (1 != 1) { // real logic here to check if the user has permission
            return $response->withRedirect($this->router->pathFor('login'));
        }
        $response = $response->withHeader('Cache-Control', 'private, max-age=10800, pre-check=10800');

        $response = $next($request, $response);

        return $response;
    }
}


#3

@tflight Hey, thanks for your answer. I was wondering, what is the clue of having it in the middleware ? I have it now in one (quite catch-all) route but adding the $response->withHeader('Cache-Control', 'private, max-age=10800, pre-check=10800') did/does not seem to work. Some internet sources refer to interference with session_start. But then again, I might not have seen the light completely …

Camiel B.


#4

@tflight

So I have now within a route code :

  $stream = new \Slim\Http\Stream(fopen($file, 'rb'));
        return $response->withHeader('Content-Type', $content_type)
                        ->withHeader('Cache-Control', 'private, max-age=10800, pre-check=10800')
                        ->withBody($stream);

And in firefox :
Cache-Control : no-store, no-cache, must-reval…ax-age=10800, pre-check=10800

So it looks very much as if the headers are added to already existing or so …

Camiel B.


#5

There were 2 issues.

  1. session_start() does impose the Cache-Control : no-store, no-cache ... Whatever you do afterwards. To get rid of this behaviour I needed session_cache_limiter('') in front of it.

Then

$stream = new \Slim\Http\Stream(fopen($file, 'rb'));
        return $response->withHeader('Content-Type', $content_type)
                        ->withHeader('Cache-Control', 'private, max-age=10800')
                        ->withBody($stream);

does the trick. At least on Chrome.

  1. However recent firefox seems to happily ignore the headers and keep asking for the resource (by the way also for resources that are just served by Apache, so really seems firefox problem). That one I could mitigate by entering an ETag and not responding when the ETag is unmodified. At least saving the bandwidth on that.