Инвалидация кеша в среде Revel

Я ищу способ аннулировать кешированный статический контент при изменении версии. Предпочтительно использовать идентификатор фиксации для аннулирования. Есть ли способ сделать это в рамках Revel?

Я бы предпочел, чтобы он был автоматическим, но я мог бы жить с его обновлением каждый раз, если это единственное место, которое мне нужно редактировать.

Текущая стратегия, которую я использую, заключается в изменении имени маршрута статического контента для включения версии, но это требует нескольких изменений. В местах, которые кажутся неестественными, например, в файле маршрутизации.


person Pablo Jomer    schedule 16.07.2015    source источник


Ответы (2)


Вы можете сделать это вручную с помощью переменной конфигурации и метода перехвата.

resourceversion.go

Создайте этот файл в папке контроллеров:

package controllers

import (
    "github.com/revel/revel"
)

// interceptor method, called before every request. 
// Sets a template variable to the resourceVersion read from app.conf
func SetVersion(c *revel.Controller) revel.Result {
    c.RenderArgs["resourceVersion"] = revel.Config.StringDefault("resourceVersion", "1")
    return nil
}

init.go

В методе init() добавьте эту строку:

revel.InterceptMethod(controllers.SetVersion, revel.BEFORE)

шаблоны

В ваших шаблонах, где вы хотите использовать версию ресурса:

<link rel="stylesheet" type="text/css" href="/public/css/style.css?{{.resourceVersion}}">

app.conf

И, наконец, место, где вы будете его обновлять — добавьте эту строку над разделом dev, чтобы применить ее к dev и prod, или используйте разные строки в каждом из них, что подходит.

resourceVersion=20150716

Я думаю, вы могли бы создать скрипт как часть процесса сборки и выпуска, который автоматически редактировал бы эту переменную конфигурации.

person Colin Nicholson    schedule 16.07.2015
comment
Можно ли использовать такой синтаксис, как /public/‹resourceVersion›/css вместо параметра запроса? - person Pablo Jomer; 16.07.2015
comment
Да, это прекрасно работает — просто измените шаблон на любой нужный формат. /public/{{.resourceVersion}}/css/style.css - person Colin Nicholson; 16.07.2015
comment
Это работает, но как мне сделать так, чтобы контент обслуживался по этому маршруту? желательно без изменения имен папок. Извините за все вопросы, хотя я совсем новичок, чтобы упиваться и идти. - person Pablo Jomer; 16.07.2015
comment
Извините, вы правы - это не работает. Вы можете попробовать добавить переменную в свой маршрут, например GET /public/:version/*filepath Static.Serve(public). Кажется, это работает, но мне кажется, что могут быть проблемы с этой переменной частью, соответствующей реальным путям, поэтому вам нужно быть немного осторожным. - person Colin Nicholson; 16.07.2015
comment
На данный момент я пытаюсь расширить статический контроллер с помощью нового контроллера, который удаляет часть версии и переходит к обычному статическому контроллеру, но у меня есть некоторые проблемы с ним. - person Pablo Jomer; 16.07.2015
comment
Моей первой мыслью было попробовать изменить статический контроллер, но мне показалось, что он изменился слишком сильно. Хотя, безусловно, это возможно, в нем не так много кода. - person Colin Nicholson; 16.07.2015
comment
Давайте продолжим обсуждение в чате. - person Pablo Jomer; 16.07.2015

Я сделал то, что предложил Колин Николсон, но также создал контроллер с именем staticversionbasedcacheinvalidator и поместил его в папку контроллера. Вы можете найти его ниже. Это позволяет вам игнорировать первую часть строки запроса, позволяя вам использовать подстановочный знак к вашей общей папке. Например, я использую эту строку конфигурации маршрута

GET     /public/*filepath                             StaticVersionbasedCacheInvalidator.Serve("public")

Затем я добавил {{.resourceVersion}} в свой маршрут вместо параметра запроса.

package controllers

import (
    "github.com/revel/revel"
    "os"
    fpath "path/filepath"
    "strings"
    "syscall"
)

type StaticVersionbasedCacheInvalidator struct {
    *revel.Controller
}

// This method handles requests for files. The supplied prefix may be absolute
// or relative. If the prefix is relative it is assumed to be relative to the
// application directory. The filepath may either be just a file or an
// additional filepath to search for the given file. This response may return
// the following responses in the event of an error or invalid request;
//   403(Forbidden): If the prefix filepath combination results in a directory.
//   404(Not found): If the prefix and filepath combination results in a non-existent file.
//   500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel.
//
// Note that when defining routes in routes/conf the parameters must not have
// spaces around the comma.
//   Bad:  StaticVersionbasedCacheInvalidator.Serve("public/img", "favicon.png")
//   Good: StaticVersionbasedCacheInvalidator.Serve("public/img","favicon.png")
//
// Examples:
// Serving a directory
//   Route (conf/routes):
//     GET /public/{<.*>filepath} StaticVersionbasedCacheInvalidator.Serve("public")
//   Request:
//     public/js/sessvars.js
//   Calls
//     StaticVersionbasedCacheInvalidator.Serve("public","js/sessvars.js")
//
// Serving a file
//   Route (conf/routes):
//     GET /favicon.ico StaticVersionbasedCacheInvalidator.Serve("public/img","favicon.png")
//   Request:
//     favicon.ico
//   Calls:
//     StaticVersionbasedCacheInvalidator.Serve("public/img", "favicon.png")
func (c StaticVersionbasedCacheInvalidator) Serve(prefix, filepath string) revel.Result {

    firstSplice := strings.Index(filepath,"/")
    if(firstSplice != -1) {
            filepath = filepath[firstSplice:len(filepath)];
    }

    // Fix for #503.
    prefix = c.Params.Fixed.Get("prefix")
    if prefix == "" {
        return c.NotFound("")
    }

    return serve(c, prefix, filepath)
}

// This method allows modules to serve binary files. The parameters are the same
// as StaticVersionbasedCacheInvalidator.Serve with the additional module name pre-pended to the list of
// arguments.
func (c StaticVersionbasedCacheInvalidator) ServeModule(moduleName, prefix, filepath string) revel.Result {
    // Fix for #503.
    prefix = c.Params.Fixed.Get("prefix")
    if prefix == "" {
        return c.NotFound("")
    }

    var basePath string
    for _, module := range revel.Modules {
        if module.Name == moduleName {
            basePath = module.Path
        }
    }

    absPath := fpath.Join(basePath, fpath.FromSlash(prefix))

    return serve(c, absPath, filepath)
}


// This method allows StaticVersionbasedCacheInvalidator serving of application files in a verified manner.
func serve(c StaticVersionbasedCacheInvalidator, prefix, filepath string) revel.Result {
    var basePath string
    if !fpath.IsAbs(prefix) {
        basePath = revel.BasePath
    }

    basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix))
    fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath))
    // Verify the request file path is within the application's scope of access
    if !strings.HasPrefix(fname, basePathPrefix) {
        revel.WARN.Printf("Attempted to read file outside of base path: %s", fname)
        return c.NotFound("")
    }

    // Verify file path is accessible
    finfo, err := os.Stat(fname)
    if err != nil {
        if os.IsNotExist(err) || err.(*os.PathError).Err == syscall.ENOTDIR {
            revel.WARN.Printf("File not found (%s): %s ", fname, err)
            return c.NotFound("File not found")
        }
        revel.ERROR.Printf("Error trying to get fileinfo for '%s': %s", fname, err)
        return c.RenderError(err)
    }

    // Disallow directory listing
    if finfo.Mode().IsDir() {
        revel.WARN.Printf("Attempted directory listing of %s", fname)
        return c.Forbidden("Directory listing not allowed")
    }

    // Open request file path
    file, err := os.Open(fname)
    if err != nil {
        if os.IsNotExist(err) {
            revel.WARN.Printf("File not found (%s): %s ", fname, err)
            return c.NotFound("File not found")
        }
        revel.ERROR.Printf("Error opening '%s': %s", fname, err)
        return c.RenderError(err)
    }
    return c.RenderFile(file, revel.Inline)
}
person Pablo Jomer    schedule 16.07.2015