【问题标题】:How to cache a JSON response in the Silverstripe Controller?如何在 Silverstripe 控制器中缓存 JSON 响应?
【发布时间】:2019-12-02 01:52:01
【问题描述】:

我们有一个 Silverstripe 4 项目,它充当无头 CMS,返回一组格式为 JSON 的复杂数据模型。

下面是代码示例:

class APIController extends ContentController
{

    public function index(HTTPRequest $request)
    {
        $dataArray['model1'] = AccessPointController::getModel1();
        $dataArray['model2'] = AccessPointController::getModel2();
        $dataArray['model3'] = AccessPointController::getModel3();
        $dataArray['model4'] = AccessPointController::getModel4();
        $dataArray['model5'] = AccessPointController::getModel5();
        $dataArray['model6'] = AccessPointController::getModel6();

        $this->response->addHeader('Content-Type', 'application/json');
        $this->response->addHeader('Access-Control-Allow-Origin', '*');

        return json_encode($dataArray);
    }

我们遇到的问题是数据模型变得如此复杂,JSON 的生成时间快到几秒钟。

只有在网站内容更新时,JSON 才会更改,因此理想情况下,我们希望缓存 JSON & 而不是为每次调用动态生成它。

上例中缓存 JSON 的最佳方式是什么?

【问题讨论】:

    标签: silverstripe silverstripe-4


    【解决方案1】:

    你看过the silverstripe docs about caching吗?
    它们确实提供了一种将事物存储在缓存中的编程方式。以及用于存储缓存的后端的配置选项。

    一个简单的例子可能是:
    我在这里延长了缓存的实时时间,但是您仍然应该注意,此缓存不是用于存储生成的静态内容,而是用于减少负载。您的应用程序仍需要每 86400 秒(24 小时)计算一次 api 响应。

    # app/_config/apiCache.yml
    ---
    Name: apicacheconfig
    ---
    # [... rest of your config config ...]
    SilverStripe\Core\Injector\Injector:
      Psr\SimpleCache\CacheInterface.apiResponseCache:
        factory: SilverStripe\Core\Cache\CacheFactory
        constructor:
          namespace: "apiResponseCache"
          defaultLifetime: 86400
    
    <?php // app/src/FooController.php
    
    class FooController extends \SilverStripe\Control\Controller {
        public function getCache() {
            return Injector::inst()->get('Psr\SimpleCache\CacheInterface.apiResponseCache');
            // or your own cache (see below):
            // return new MyCache();
        }
    
        protected function hasDataBeenChanged() {
            // alternative to this method, you could also simply include Page::get()->max('LastEdited') or whatever in your cache key inside index(), but if you are using your own cache system, you need to handle deleting of old unused cache files. If you are using SilverStripe's cache, it will do that for you
            $c = $this->getCache();
            $lastCacheTime = $c->has('cacheTime') ? (int)$c->get('cacheTime') : 0;
            $lastDataChangeTime = strtotime(Page::get()->max('LastEdited'));
            return $lastDataChangeTime > $lastCacheTime;
        }
    
        public function index() {
            $c = $this->getCache();
            $cacheKey = 'indexActionResponse';
            if ($c->has($cacheKey) && !$this->hasDataBeenChanged()) {
                $data = $c->get($cacheKey);
            } else {
                $dataArray['model1'] = AccessPointController::getModel1();
                $dataArray['model2'] = AccessPointController::getModel2();
                $dataArray['model3'] = AccessPointController::getModel3();
                $dataArray['model4'] = AccessPointController::getModel4();
                $dataArray['model5'] = AccessPointController::getModel5();
                $dataArray['model6'] = AccessPointController::getModel6();
                $data = json_encode($dataArray);
                $c->set($cacheKey, $data);
                $c->set('cacheTime', time());
            }
    
            $this->response->addHeader('Content-Type', 'application/json');
            $this->response->addHeader('Access-Control-Allow-Origin', '*');
    
            return json_encode($dataArray);
        }
    }
    

    如果您正在寻找永久/持久缓存,它只会在数据更改时更新,我建议您寻找不同的后端,或者自己实现一个简单的缓存并使用它而不是 silverstripe 缓存。

    class MyCache {
        protected function fileName($key) {
            if (strpos($key, '/') !== false || strpos($key, '\\') !== false) {
                throw new \Exception("Invalid cache key '$key'");
            }
            return BASE_PATH . "/api-cache/$key.json";
        }
    
        public function get($key) {
            if ($this->has($key)) {
                return file_get_contents($this->fileName($key));
            }
            return null;
        }
    
        public function set($key, $val) {
            file_put_contents($this->fileName($key), $val);
        }
    
        public function has($key) {
            $f = $this->fileName($key);
            return @file_exists($f);
        }
    

    【讨论】:

    • 感谢您提供如此出色、详细的答案。我已经养成了不看 SS 文档的坏习惯,因为十年前我开始使用 SS 时,文档已经很糟糕,甚至根本不存在。我知道最近在文档上付出了很多努力,并且会努力更频繁地使用它。
    猜你喜欢
    • 2018-09-12
    • 1970-01-01
    • 2012-07-27
    • 1970-01-01
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    相关资源
    最近更新 更多