Başvuru
Yapılandırma
collage.Config ve alt yapılarının her alanı, varsayılan değerleri ve doğrulamanın neyi denetlediği.
Bir uygulama, collage.New'a verilen tek bir collage.Config değeriyle
yapılandırılır. Her alanın kullanılabilir bir sıfır değeri vardır; bu yüzden bir
yapılandırma yalnızca varsayılanlardan farklı olanı söyler:
app, err := collage.New(&collage.Config{
Template: collage.TemplateConfig{Root: "templates"},
})
New bu değerle sırasıyla üç şey yapar:
- Varsayılanları doldurur.
cfg.ApplyDefaults()'u çağırır; bu da varsayılanı olan ve sıfır değerde duran her alanı ayarlar. Verdiğiniz değeri değiştirir; böylece uygulamanın tam olarak neyle kurulduğunu geri okuyabilirsiniz. - Doğrular.
cfg.Validate()'i çağırır ve bulduğu ilk sorunu hata olarak döner — bkz. Doğrulama. - Uygulamayı kurar. Var olmayan bir şablon kökü, ayrıştırılamayan bir şablon, bir
plugin'in
Configure'unun başarısız olması — her biri ilk istekte değil, burada bildirilir.
nil vermek ErrNilConfig'dir. nil bir yapılandırma varsayılanları istemek değil,
bir hatadır; çünkü şablonlarınızın nerede olduğunu yapılandırma söyler.
Config
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
DevMode |
bool |
false |
Framework genelinde geliştirme modu. |
DevWatch |
[]string |
yok | Değiştiğinde geliştirme sayfasını yenileyen ek dizinler. v0.10.0'dan itibaren. |
Logger |
*slog.Logger |
başlangıçta seçilir | Framework'ün ve plugin'lerin yazdığı logger. |
Server |
ServerConfig |
HTTP sunucusu. | |
Security |
SecurityConfig |
İstek sahteciliği koruması. | |
Template |
TemplateConfig |
Şablon yükleme ve render etme. | |
Cache |
CacheConfig |
Sayfa önbelleği. | |
Locale |
LocaleConfig |
URL'lerin hangi locale'leri taşıdığı. | |
TrailingSlash |
bool |
false |
Her sayfanın URL'sini / ile bitirir. v0.13.0'dan itibaren. |
Observability |
ObservabilityConfig |
Metrikler ve izleme (tracing). | |
Plugins |
[]Plugin |
yok | Uygulama kurulurken kaydedilen plugin'ler. |
PluginConfig |
map[string]json.RawMessage |
yok | Plugin adına göre anahtarlanmış, her plugin'in kendi yapılandırması. |
DevMode
Geliştirme modunu açar: şablonlar her render'dan önce diskten yeniden yüklenir,
sayfalar tarayıcıda kendilerini yeniler, başarısız fragment'ler sayfada gösterilir,
sayfa önbelleğinden hiç okunmaz, disk önbelleğinin yerini bellek alır, DevWatch'ta
adı geçen dizinler izlenir ve yerleşik hata sayfası hatanın başladığı fragment'in
adını verip hata zincirinin tamamını gösterir. Production'da kapalı olmalıdır —
bu tanılama bilgileri yolları, sunucu adlarını ve hata mesajlarının içerdiği her şeyi
taşır.
Geçerli değer cfg.IsDevMode()'dur; bu da DevMode || Template.DevMode'dur. İskelet
olarak oluşturulan bir proje bunu COLLAGE_DEV=1'den ayarlar; collage dev bunu
sizin için ayarlar.
DevWatch
Şablonlar ve mount'lar dışında, değiştiğinde geliştirme sayfasını yenileyen dizinler — uygulamanızın diskten kendisi okuduğu Markdown ya da JSON gibi içerikler için. v0.10.0'dan itibaren.
app, err := collage.New(&collage.Config{
DevMode: os.Getenv("COLLAGE_DEV") == "1",
DevWatch: []string{"content"},
// ...
})
Var olmayan bir dizin hata sayılmaz, atlanır; çünkü aynı yapılandırma binary nerede başlatılırsa orada çalışır. Geliştirme dışında yok sayılır. Bir dizini izlemek yalnızca tarayıcıyı yeniler; yeni içeriği bir sonraki render'da — başlangıçta yüklenmiş bir kopyadan değil — okumak sizin kodunuzun işidir.
Logger
Varsayılan olan nil, seçimi uygulamayı kurarken framework'e bırakır: bir
terminalde — ve yalnızca slog'un varsayılan handler'ını hiçbir şey değiştirmemişse —
bir insan için tasarlanmış, her kayıt için tek satır ve renkli bir seviye işaretiyle
yazan derli toplu bir handler. Diğer her yerde, değiştirilmeden slog.Default().
slog.SetDefault'u çağırmış bir uygulama kendi handler'ını korur. Emin olmak için
bir logger verin; örneğin bir makinenin okuyacağı loglar için bir JSON handler'ı.
ApplyDefaults bu alanı nil bırakır.
Plugins ve PluginConfig
Plugins, New çalışırken sırayla kaydedilir. Şablonlar ayrıştırılmadan önce
harekete geçmesi gereken — bir şablon fonksiyonu eklemek ya da mount'ları sarmalamak
için — bir plugin, app.RegisterPlugin üzerinden değil, buradan gelmelidir.
PluginConfig her plugin'in bölümünü ham JSON olarak tutar. Framework hiçbir dosya
okumaz: bunu dilediğiniz gibi doldurun ya da eksik bir dosya için nil dönen
collage.LoadPluginConfig("plugins-config.json")'u kullanın. Kayıtlı hiçbir plugin'i
adlandırmayan bir anahtar, uygulamanın ErrUnknownPluginConfig ile başlamasını
engeller. Bkz. Plugin kullanmak.
TrailingSlash
Her sayfanın tek bir adresi vardır; TrailingSlash bu adresin / ile bitip
bitmediğini söyler. Açıkken sayfa /blog/hello/ adresindedir ve /blog/hello oraya
301 ile yönlendirilir; varsayılan olan kapalı durumda tersi geçerlidir. Adla
kurulan bağlantılar — pageURL, pageURLIn, localeURL, app.URL — seçilen
yazımla çıkar; bir locale'in ana sayfası da buna dahildir: açıkken /tr/,
kapalıyken /tr.
Statik bir barındırma hizmetine dışa aktardığınız bir
site için açın. Dışa aktarma bir sayfayı <path>/index.html olarak yazar; barındırma
hizmeti bu dosyayı /blog/hello/ adresinde sunar, /blog/hello adresinden ise
ancak bir yönlendirmeyle ulaşır. Yani ayar kapalıyken her canonical bağlantı, her
sitemap girdisi ve her iç bağlantı bir yönlendirmeyi gösterir.
Ayar sayfalara uygulanır. Bir document bir dosyadır ve yolunu
yazıldığı gibi korur, iki durumda da /sitemap.xml; bir action ise hangi yazıma
gönderildiyse orada yanıtlanır.
ServerConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
Host |
string |
"localhost" |
Sunucunun dinlediği adres. |
Port |
int |
3000 |
TCP portu, 1–65535. |
ReadTimeout |
time.Duration |
15s |
Bir isteği okumanın ne kadar sürebileceği. |
WriteTimeout |
time.Duration |
30s |
Bir yanıtı yazmanın ne kadar sürebileceği. |
IdleTimeout |
time.Duration |
60s |
Bir keep-alive bağlantısının ne kadar boşta bekleyebileceği. |
ShutdownTimeout |
time.Duration |
10s |
Düzgün kapanışın süren istekleri ne kadar beklediği. |
MaxBodyBytes |
int64 |
4 MiB | Action kendisi bir sınır koymadığında, action'ın istek gövdesine uygulanan sınır. |
Host'un varsayılanı localhost'tur ve makinenin dışından erişilemez — bir
container'da bunu 0.0.0.0 yapın. MaxBodyBytes'ı ApplyDefaults doldurmaz: sıfır,
bir istek geldiğinde uygulanan yerleşik 4 MiB (4 << 20 bayt) anlamına gelir; negatif
bir değer ise sınırsız demektir. Sınırsız bir gövde, boyutunu anonim bir çağıranın
seçtiği bellektir; bunu bilinçli olarak seçin. Bir action kendi sınırını
WithMaxBodyBytes ile koyabilir; bkz. Formlar ve action'lar.
SecurityConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
CSRFKey |
[]byte |
her süreç için üretilir | Sahtecilik token'larını imzalayan anahtar. |
CSRFCookieName |
string |
"collage_csrf" |
Token'ın taşındığı cookie. |
CSRFFieldName |
string |
"_csrf" |
Token'ın gönderildiği form alanı. |
CSRFHeaderName |
string |
"X-CSRF-Token" |
Token'ın bunun yerine gönderilebileceği header. |
DisableCSRF |
bool |
false |
Sahtecilik denetimini tüm uygulama için kapatır. |
Form içeren herhangi bir şeyi yayına almadan önce CSRFKey'i ayarlayın. En az
32 rastgele bayt olmalı, diğer gizli bilgilerinizle birlikte saklanmalı ve her
instance'ta aynı olmalıdır. Boş bırakılırsa her süreç için bir anahtar üretilir: ilk
çalıştırma için sorun değildir, yayına almak içinse yanlıştır; çünkü yeniden
başlatmadan önce — ya da başka bir instance tarafından — verilmiş bir token reddedilir.
Uygulama bir anahtar ürettiğini loglar — geliştirme dışında uyarı, geliştirmede info
seviyesinde — ve bunu yalnızca güvenli olmayan bir metoda (POST, PUT, PATCH,
DELETE) yanıt veren ve WithoutCSRF ile muaf tutulmamış bir action varsa yapar;
çünkü token'ı yalnızca böyle bir action doğrular (WithoutCSRF muafiyeti v0.11.0'dan
itibaren). Anahtar disk önbelleğinin ad alanına dahil değildir, bu yüzden önbellek yeni
bir anahtardan etkilenmeden kalır; içinde form olan ve eski anahtarla saklanmış bir
önbellekteki sayfa ise sunulmaz, yeniden render edilir — bkz.
Önbellekleme. İskelet olarak oluşturulan main.go onu
COLLAGE_CSRF_KEY'den okur; openssl rand -hex 32 bir tane üretir.
Yukarıdaki ad varsayılanlarını ApplyDefaults değil, sahtecilik koruması uygular; bu
yüzden alanlar Config'inizde boş kalır. CSRFFieldName alanı iki tarafta da yeniden
adlandırır: {{csrfToken}} denetimin okuduğu adı yazar, formlarda değişiklik gerekmez.
Token'ı formdan okuyan script'ler — input[name="_csrf"] — yeni adı kullanmak
zorundadır.
DisableCSRF, tarayıcıdan gönderilen hiçbir formu olmayan bir uygulama içindir —
kendi kimlik doğrulamasının arkasındaki bir API. Ayarlandığında {{csrfToken}},
gönderimi hiçbir anlam taşımayacak bir form render etmek yerine render'ı başarısız
kılar. Bunun yerine tek bir action'ı muaf tutmak için o action'da WithoutCSRF
kullanın.
TemplateConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
FS |
fs.FS |
nil |
Şablonların yüklendiği dosya sistemi; nil disk demektir. |
Root |
string |
diskte "./templates" |
Şablonların yüklendiği, her şablon adından çıkarılan dizin. |
Extension |
string |
".html" |
Şablon dosyalarının uzantısı. |
Funcs |
template.FuncMap |
yok | Yerleşiklerin üzerine birleştirilen ek şablon fonksiyonları. |
DevMode |
bool |
false |
Şablonları her render'da diskten yeniden yükler. |
Timeout |
time.Duration |
5s |
Varsayılan data handler zaman aşımı ve her document handler'ının toplam süre bütçesi. |
FS ve Root
FS nil iken Root çalışma dizinine göre diskteki bir yoldur ve varsayılanı
./templates'tir. FS ayarlıyken Root onun içinde eğik çizgiyle ayrılmış bir
dizindir ve varsayılanı yoktur — boş bir Root, FS'in kendi kökü demektir.
Bir binary'nin herhangi bir dizinden çalışabilmesini sağlayan şey gömmektir:
//go:embed all:templates
var templates embed.FS
app, err := collage.New(&collage.Config{
Template: collage.TemplateConfig{FS: templates, Root: "templates"},
})
Geliştirmede gömülü bir küme değişemeyeceği için collage, oradaysa diskteki dizini
tercih eder. Var olmayan bir Root, New'dan ErrTemplateRootMissing döner; altında
olup — bir symlink üzerinden — dışına çözümlenen bir şablon ise
ErrTemplateEscapesRoot'tur.
Funcs
Şablonlar ayrıştırılırken yerleşik fonksiyonlara eklenir; yerleşik bir adla girilen
bir kayıt o yerleşiğin yerini alır, bir plugin'in eklediği adla girilen bir kayıt da
plugin'inkinin. New'dan önce ayarlanmalıdır; çünkü bir şablon yalnızca
ayrıştırıldığı sırada var olan bir fonksiyonu çağırabilir — bilinmeyen bir adı çağıran
şablon New'da başarısız olur. Her render'a bağlanan fonksiyonlar (slot, hoist,
asset, stylesheet, csrfToken, pageURL, pageURLIn, localeURL) her render'da
yeniden bağlanır; bu yüzden onları geçersiz kılmanın hiçbir etkisi yoktur. Bkz.
Şablon fonksiyonları.
Timeout
Fragment'i WithTimeout ile bir süre koymamış bir data handler'ın son süresi. Aynı
zamanda kendi zaman aşımı olmayan bir document handler'ının tek
sınırıdır — dolayısıyla yavaş tek bir fragment için onu artırmak, her sitemap ve feed
için de artırır. collage'daki her zaman aşımı gibi, handler'ın aldığı context'i
sınırlar; ctx.Done()'ı hiç denetlemeyen bir handler bu süreyi aşabilir.
CacheConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
Enabled |
bool |
false |
Sayfa önbelleğinin ana şalteri. |
Store |
Cache |
nil |
Kendi önbellek gerçeklemeniz. |
Type |
string |
etkinse "memory" |
Store nil iken yerleşik önbellek: "memory" ya da "disk". |
DefaultTTL |
time.Duration |
5m |
Sayfa bir süre koymadığında kaydın ömrü. |
MaxEntries |
int |
10000 |
Önbellek kaydı sayısının üst sınırı; negatif sınırsız demektir. |
Dir |
string |
yok | Disk önbelleğinin kayıtları sakladığı yer. "disk" için zorunlu. |
Version |
string |
binary'den türetilir | Disk önbelleğindeki kayıtların hangi build'e ait olduğunu belirler. |
MaxKeysPerTag |
int |
10000 |
Tek bir etiket altında tutulan önbellek anahtarı sayısının üst sınırı; negatif sınırsız demektir. |
Önbellekleme varsayılan olarak kapalıdır. Enabled false iken, bir Store
ayarlı olsa bile hiçbir şey önbelleğe alınmaz. Type'ın varsayılanı yalnızca Enabled
true ve Store nil olduğunda "memory"'dir.
Bir Store, doğrulama da dahil olmak üzere Type'ın yerini tamamen alır.
Sayfaları Redis'e ya da başka bir yere koymak için collage.Cache'i (Get, Set,
Invalidate, InvalidateKey, Clear) gerçekleyin; ayrıca collage.TaggedCache'i de
gerçekleyen bir depoya her kaydın etiketleri yazma sırasında verilir. Somut bir tipin
nil pointer'ını atamak yerine alanı boş bırakın; o, ilk aramada panic'e yol açan nil
olmayan bir interface'tir.
Disk önbelleği süreçten uzun yaşar. Kayıtları Dir'in, Version'ın hash'iyle
adlandırılmış bir alt dizininde durur; böylece yeni bir build farklı bir dizini okur
ve bayat hiçbir şey bulmaz. Version'ı boş bırakırsanız çalışan executable'ın hash'i
olur; bu da tam olarak çıktının değişebileceği anda değişir. Sayfaların nasıl
görüneceğine binary'nin dışındaki bir şey karar veriyorsa onu ayarlayın — bir commit,
bir sürüm etiketi. Executable'ın hash'i alınamıyorsa bir uyarıyla birlikte bellek içi
bir önbellek kullanılır — v0.11.0'dan itibaren dizin oluşturulamadığında da (örneğin
salt okunur bir dosya sisteminde) böyledir: collage.New başarısız olmak yerine uyarır
ve bellekle devam eder. Uygulama çalışırken başarısız olan bir yazma loglanır ve sayfa
önbelleğe alınmadan sunulur. Geliştirmede disk önbelleği hiç kullanılmaz: yerini bellek
içi bir önbellek alır.
MaxEntries ayrıca collage.Cached'in istekler arasında tuttuğu değerleri de
sınırlar. MaxKeysPerTag ise framework'ün bağımlılık izleyicisini, yani bir
etiketi önbellek anahtarlarına geri eşleyen süreç başına dizini sınırlar. Her farklı
query string farklı bir anahtardır ve önbellek bir kaydı çıkardığında ya da kaydın
süresi dolduğunda hiçbir şey anahtarı izleyiciden silmez; bu yüzden bir üst sınır
olmadan bir istemci bu dizini sınırsızca büyütebilir. Bir etiket sınıra ulaştığında
en eski anahtarı yalnızca izleyiciden düşürülür, önbellekten değil.
Bunun bedeli depoya bağlıdır. Yerleşik bellek ve disk önbellekleri etiketleri kendileri
dizinler (TaggedCache'i gerçeklerler); bu yüzden InvalidateTags tuttukları her
kayda yine ulaşır; yalnızca InvalidateTagsN'in bildirdiği sayı — izleyicinin
çözümlediği sayı — daha düşük çıkabilir. TaggedCache'i gerçeklemeyen özel bir Store
yalnızca izleyiciye dayanır ve onun için düşürülen bir anahtar, InvalidateTags'in
artık ulaşamadığı bir kayıttır — süresi dolana kadar sunulur. Böyle bir depoyla, sınırı
herhangi bir etiketin kapsayabileceği canlı kayıt sayısının üzerine ayarlayın.
İki sınır için de sıfır varsayılan demektir; yalnızca negatif bir değer sınırsız demektir. Bkz. Önbellekleme.
LocaleConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
Default |
string |
"en" |
Locale öneki olmayan bir URL'nin locale'i. |
Supported |
[]string |
[Default] |
Uygulamanın sunduğu her locale. |
DisablePathLocale |
bool |
false |
Locale'i yoldan çözümlemeyi bırakır; her istek Default'tadır. |
PrefixDefault |
bool |
false |
Default'un sayfalarına da önek verir: /en/about. v0.14.0'dan itibaren. |
Locale'i seçen tek şey URL'dir: /about Default'tadır, /tr/hakkinda ise "tr"'de.
collage hiçbir zaman Accept-Language'ten ya da bir cookie'den locale seçmez; çünkü
farklı okuyuculara farklı şeyler ifade eden bir URL'yi önbellekler, crawler'lar ve
paylaşılan bağlantılar yanlış anlar. Varsayılan locale'in kendi öneki olan /en/about,
/about'a kalıcı olarak yönlendirilir — PrefixDefault ayarlanmadıkça; o zaman
adres /en/about olur ve /about oraya yönlendirilir. Bkz.
Bağlantılar ve locale'ler. Dil seçimini müzakere etmek istiyorsanız bunu
middleware'de yapın: Türkçe bir tarayıcıyı /tr'ye yönlendirin ya da tek bir URL'yi
her dil için render edip bunu collage.Vary ile bildirin. Bkz.
Bağlantılar ve locale'ler.
ObservabilityConfig
| Alan | Tip | Varsayılan | Anlamı |
|---|---|---|---|
Metrics |
Metrics |
işlemsiz | Sayaçları ve süreleri alır. |
Tracer |
Tracer |
işlemsiz | İstekler, render'lar ve fragment'ler etrafında span başlatır. |
İkisi de collage'ı kendi altyapınıza bağlamak için gerçeklediğiniz interface'lerdir;
nil hiçbir şey yapmayan (no-op) bir gerçekleme demektir.
type Metrics interface {
RenderDuration(ctx context.Context, page string, d time.Duration, cacheHit bool)
FragmentDuration(ctx context.Context, page, fragment string, d time.Duration, err error)
CacheEvent(ctx context.Context, event collage.CacheEvent, key string)
HTTPResponse(ctx context.Context, status int, path string, d time.Duration)
Invalidation(ctx context.Context, tags []string, keys int)
}
type Tracer interface {
StartSpan(ctx context.Context, name string) (context.Context, collage.Span)
}
type Span interface {
SetAttribute(key, value string)
RecordError(err error)
End()
}
CacheEvent; collage.CacheHit, CacheMiss, CacheSet, CacheEvict,
CacheInvalidate ve CacheCoalesced'ten biridir — sonuncusu, bir isteğin aynı
anahtar için zaten çalışmakta olan bir render tarafından karşılandığı anlamına gelir.
Doğrulama
Validate, ApplyDefaults çalıştıktan sonra bunlardan bulduğu ilkini döner:
| Koşul | Hata |
|---|---|
Server.Port 1–65535 aralığının dışında |
ErrInvalidPort |
Template.Root boş ve Template.FS nil |
ErrEmptyTemplateRoot |
Cache.Enabled, Store yok ve Type ne "memory" ne "disk" |
ErrInvalidCacheType |
Cache.Enabled, Store yok, Type "disk" ve Cache.Dir boş |
ErrEmptyCacheDir |
Locale.Default boş |
ErrEmptyLocaleDefault |
Locale.Default, Locale.Supported içinde değil |
ErrLocaleDefaultNotSupported |
Negatif bir Server.ReadTimeout, WriteTimeout, IdleTimeout, ShutdownTimeout, Template.Timeout ya da Cache.DefaultTTL |
ErrNegativeDuration |
Negatif süre hatası altı alanın hepsi için tek bir sentinel'dir; mesaj başarısız olan
alanı adlandırır, örneğin server.read_timeout. Varsayılanlar önce uygulandığı için
sıfır bir port ya da boş bir varsayılan locale New'dan doğrulamaya hiç ulaşmaz —
yalnızca açıkça ayarladığınız bir değer başarısız olabilir. Bunları errors.Is ile
eşleştirin; tam liste Hatalar sayfasındadır.
İkisini de kendiniz çağırabilirsiniz — örneğin bir testte bir yapılandırmayı denetlemek için:
cfg := collage.Config{Server: collage.ServerConfig{Port: 70000}}
cfg.ApplyDefaults()
err := cfg.Validate() // wraps collage.ErrInvalidPort
ApplyDefaults idempotent'tir: varsayılanları zaten olan bir yapılandırmaya
uygulamak hiçbir şeyi değiştirmez.
Eksiksiz bir yapılandırma
İskelet olarak oluşturulan bir projenin başladığı yapılandırma; ortama göre değişen kısımlar ortamdan okunur:
pluginConfig, err := collage.LoadPluginConfig("plugins-config.json")
if err != nil {
return nil, fmt.Errorf("plugin configuration: %w", err)
}
app, err := collage.New(&collage.Config{
DevMode: os.Getenv("COLLAGE_DEV") == "1",
Server: collage.ServerConfig{
Host: envString("HOST", "localhost"),
Port: envInt("PORT", 3000),
},
Template: collage.TemplateConfig{
FS: templatesFS,
Root: "templates",
Extension: ".html",
},
Cache: collage.CacheConfig{
Enabled: true,
Type: "disk",
Dir: ".cache",
DefaultTTL: 5 * time.Minute,
},
PluginConfig: pluginConfig,
Security: collage.SecurityConfig{
CSRFKey: []byte(os.Getenv("COLLAGE_CSRF_KEY")),
},
})
envString ve envInt, iskelet main.go'daki, bir değişkeni okuyup yoksa bir
varsayılana dönen iki küçük yardımcıdır.