Merge pull request #740 from mistadikay/master
Fix failing build for Chinese, Japanese and German versions
This commit is contained in:
58
de/02.2.md
58
de/02.2.md
@@ -15,12 +15,12 @@ Definiere mehrere Variablen.
|
||||
|
||||
// Definiere drei Variablen vom Typ "type"
|
||||
var vname1, vname2, vname3 type
|
||||
|
||||
|
||||
Definiere eine Variable mit einem Startwert.
|
||||
|
||||
// Definiere eine Variable mit dem Namen “variableName” vom Typ "type" und dem Wert "value"
|
||||
var variableName type = value
|
||||
|
||||
|
||||
Definiere mehrere Variablen mit einem Startwert.
|
||||
|
||||
/*
|
||||
@@ -28,7 +28,7 @@ Definiere mehrere Variablen mit einem Startwert.
|
||||
vname1 ist gleich v1, vname2 ist gleich v2, vname3 ist gleich v3
|
||||
*/
|
||||
var vname1, vname2, vname3 type = v1, v2, v3
|
||||
|
||||
|
||||
Findest Du es nicht auch ein wenig mühsehlig, Variablen auf diesem Weg zu definieren? Keine Sorge, denn das Team hinter Go hat auch so gedacht. Daher kannst Du Variablen Startwerte geben, ohne explizit den Datentyp zu definieren. Der Code würde dann so aussehen:
|
||||
|
||||
/*
|
||||
@@ -36,7 +36,7 @@ Findest Du es nicht auch ein wenig mühsehlig, Variablen auf diesem Weg zu defin
|
||||
vname1 ist gleich v1, vname2 ist gleich v2, vname3 ist gleich v3
|
||||
*/
|
||||
var vname1, vname2, vname3 = v1, v2, v3
|
||||
|
||||
|
||||
Schön, ich weiß das dies immer noch nicht einfach genug für Dich ist. Mal schauen, wie wir das lösen können.
|
||||
|
||||
/*
|
||||
@@ -44,7 +44,7 @@ Schön, ich weiß das dies immer noch nicht einfach genug für Dich ist. Mal sch
|
||||
vname1 ist gleich v1,vname2 ist gleich v2,vname3 ist gleich v3
|
||||
*/
|
||||
vname1, vname2, vname3 := v1, v2, v3
|
||||
|
||||
|
||||
|
||||
So sieht es schon viel besser aus. Nutze `:=`, um `var` und `type` zu ersetzen. Es handelt sich hierbei um eine Kurzschreibweise. Aber warte, es gibt einen kleinen Haken: diese Form der Definition kann nur innerhalb von Fuktionen genutzt werden. Der Compiler wird sonst eine Fehlermeldung ausgeben, wenn Du es trotzdem außerhalb der `{}` einer Funktion versuchst. Daher nutzen wir meist das Schlüsselwort `var` um globale Variablen zu definieren oder die Funktion `var()`.
|
||||
|
||||
@@ -59,7 +59,7 @@ Wenn Du Variablen in Deinem Programm definierst, aber keine Verwendung finden, w
|
||||
func main() {
|
||||
var i int
|
||||
}
|
||||
|
||||
|
||||
## Konstanten
|
||||
|
||||
Konstanten sind Werte, die während der Kompilierung festgelegt werden und während der Laufzeit nicht veränderbar sind. In Go kannst Du Konstanten als Wert Nummern, Booleans oder Strings geben.
|
||||
@@ -67,7 +67,7 @@ Konstanten sind Werte, die während der Kompilierung festgelegt werden und währ
|
||||
Definiere eine Konstante wie folgt.
|
||||
|
||||
const constantName = value
|
||||
// Du kannst auch den Datentyp hinzufügen, wenn nötig
|
||||
// Du kannst auch den Datentyp hinzufügen, wenn nötig
|
||||
const Pi float32 = 3.1415926
|
||||
|
||||
Mehr Beispiele.
|
||||
@@ -76,7 +76,7 @@ Mehr Beispiele.
|
||||
const i = 10000
|
||||
const MaxThread = 10
|
||||
const prefix = "astaxie_"
|
||||
|
||||
|
||||
## Grundlegende Datentypen
|
||||
|
||||
### Boolean
|
||||
@@ -91,7 +91,7 @@ In Go nutzen wir `bool`, um Booleans (Wahrheitswerte) auszudrücken, die entwede
|
||||
valid := false // Kurzschreibweise einer Definition
|
||||
available = true // Eine Variable deklarieren
|
||||
}
|
||||
|
||||
|
||||
### Numerische Datentypen
|
||||
|
||||
Der Datentyp Integer (ganze Zahlen) kann sowohl den positiven, als auch den negativen Zahlenraum umfassen, was durch ein Vorzeichen kenntlich gemacht wird. Go besitzt `int` und `uint`, welche den selben Wertebereich haben. Dessen Größe hängt aber vom Betriebssystem ab. Es werden 32-Bit unter 32-Bit Betriebssystemen verwendet und 64-Bit unter 64-Bit Betriebssystemen. Go umfasst außerdem Datentypen mit einer spezifischen Länge: `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32` und `uint64`. Bedenke, dass `rune` ein Alias für `int32` ist und `byte` dem `uint8` gleicht.
|
||||
@@ -114,7 +114,7 @@ War das schon alles? Nein! Go unterstützt auch komplexe Zahlen. `complex128` (b
|
||||
var c complex64 = 5+5i
|
||||
// Ausgabe: (5+5i)
|
||||
fmt.Printf("Der Wert ist: %v", c)
|
||||
|
||||
|
||||
### Strings
|
||||
|
||||
Wir sprachen vorhin darüber, das Go eine native UTF-8 Unterstützung mit sich bringt. Strings (Zeichenketten) werden durch Anführungszeichen `""` gekennzeichet, oder durch Backticks (rückwärts geneigtes Hochkomma) ``` `` ```.
|
||||
@@ -140,7 +140,7 @@ Aber was ist, wenn ich wirklich nur ein Zeichen in einem String ändern möchte?
|
||||
c[0] = 'c'
|
||||
s2 := string(c) // Wandle den Wert in eine String zurück
|
||||
fmt.Printf("%s\n", s2)
|
||||
|
||||
|
||||
|
||||
Nutze den `+` Operator, um zwei Strings zu verknüpfen.
|
||||
|
||||
@@ -148,20 +148,20 @@ Nutze den `+` Operator, um zwei Strings zu verknüpfen.
|
||||
m := " Welt"
|
||||
a := s + m
|
||||
fmt.Printf("%s\n", a)
|
||||
|
||||
|
||||
|
||||
oder auch
|
||||
|
||||
s := "Hallo"
|
||||
s = "c" + s[1:] // Du kannst die Werte mit Hilfe des Index nicht verändern, aber sie abfragen
|
||||
fmt.Printf("%s\n", s)
|
||||
|
||||
|
||||
|
||||
Was ist, wenn ein String über mehrere Zeilen verlaufen soll?
|
||||
|
||||
m := `Hallo
|
||||
Welt`
|
||||
|
||||
|
||||
`\`` wird die Zeichen in einem String escapen (d.h. mit `\` dessen Ausführung verhindern).
|
||||
|
||||
### Fehlermeldungen
|
||||
@@ -172,7 +172,7 @@ Go besitzt mit `error` einen eigenen Datentyp, um mit Fehlermeldungen umzugehen.
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
}
|
||||
|
||||
|
||||
### Grundeliegende Datenstrukturen
|
||||
|
||||
Die folgende Grafik enstammt dem Artikel [Datenstrukturen in Go](http://research.swtch.com/godata) (auf englisch) aus [Russ Coxs Blog](http://research.swtch.com/). Wie Du sehen kannst, nutzt Go Abschnitte des Arbeitsspeichers, um Daten zu speichern.
|
||||
@@ -199,7 +199,7 @@ Wenn Du mehrere Konstanten und Variablen deklarieren oder Pakete importieren mö
|
||||
var i int
|
||||
var pi float32
|
||||
var prefix string
|
||||
|
||||
|
||||
Gruppierter Ansatz.
|
||||
|
||||
import(
|
||||
@@ -233,8 +233,8 @@ Go besitzt das Schlüselwort `iota`, um eine Aufzählung zu starten. Der Startwe
|
||||
)
|
||||
|
||||
const v = iota // Sobald iota erneut auf `const` trifft, wird erneut mit `0` gestartet, also gilt v = 0.
|
||||
|
||||
const (
|
||||
|
||||
const (
|
||||
e, f, g = iota, iota, iota // e, f und g haben den selben Wert 0, da sie in der selben Zeile stehen.
|
||||
)
|
||||
|
||||
@@ -261,7 +261,7 @@ wobei in `[n]Datentyp` das `n` die Länge des Arrays angibt. `Datentyp` ist selb
|
||||
fmt.Printf("Das erste Element ist %d\n", arr[0]) // Beim Auslesen des Wertes wird 42 zurückgegeben
|
||||
fmt.Printf("Das letzte Element ist %d\n", arr[9]) // Es gibt den Standardwert des 10. Elements zurück, was in diesem Fall 0 ist.
|
||||
|
||||
Da die Länge ein Teil des Array-Typs ist, sind `[3]int` und `[4]int` verschieden, sodass wir die Länge eines Arrays nicht ändern können. Nutzt Du Arrays als Argument in einer Funktion, dann wird eine Kopie des Arrays übergeben, statt einem Zeiger (bzw. ein Verweis) auf das Original. Möchtest Du stattdessen den Zeiger übergeben, dann musst Du einen `slice` verwenden. Darauf gehen wir aber später nochmals ein.
|
||||
Da die Länge ein Teil des Array-Typs ist, sind `[3]int` und `[4]int` verschieden, sodass wir die Länge eines Arrays nicht ändern können. Nutzt Du Arrays als Argument in einer Funktion, dann wird eine Kopie des Arrays übergeben, statt einem Zeiger (bzw. ein Verweis) auf das Original. Möchtest Du stattdessen den Zeiger übergeben, dann musst Du einen `slice` verwenden. Darauf gehen wir aber später nochmals ein.
|
||||
|
||||
Es ist möglich, `:=` zu nutzen, um ein Array zu deklarieren.
|
||||
|
||||
@@ -273,10 +273,10 @@ Vielleicht möchtest Du auch Arrays als Element in einem Array nutzen. Schauen w
|
||||
|
||||
// Deklariere ein zweidimensionales Array mit zwei Elementen, welche jeweils vier Elemente besitzen.
|
||||
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
|
||||
|
||||
|
||||
|
||||
// Die Dekleration kann auch ein wenig kompakter geschrieben werden.
|
||||
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
|
||||
|
||||
{% raw %} easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} {% endraw %}
|
||||
|
||||
Arrays sind grundlegende Datenstrukturen in Go.
|
||||
|
||||
@@ -308,12 +308,12 @@ Nun deklarieren wir ein `slice` und vergeben Startwerte.
|
||||
// 'a' verweist auf die Elemente zwischen Index 3 und 5 im Array ar.
|
||||
a = ar[2:5]
|
||||
// 'a' besitzt nun die Elemente ar[2],ar[3] und ar[4]
|
||||
|
||||
|
||||
// 'b' ist ein weiterer Ausschnitt (Slice) vom Array ar
|
||||
b = ar[3:5]
|
||||
// 'b' besitzt nun die Elemente ar[3] und ar[4]
|
||||
|
||||
Beachte die Unterscheide bei der Deklaration von `slice` und `array`. Wir nutzen `[…]`, um Go die Länge automatisch herausfinden zu lassen, aber nutzen `[]` lediglich zur Deklaration von Slices.
|
||||
Beachte die Unterscheide bei der Deklaration von `slice` und `array`. Wir nutzen `[…]`, um Go die Länge automatisch herausfinden zu lassen, aber nutzen `[]` lediglich zur Deklaration von Slices.
|
||||
|
||||
Ihre zugrundeliegenden Datentypen.
|
||||
|
||||
@@ -325,7 +325,7 @@ Slices haben bestimmte Anwendungsgebiete.
|
||||
|
||||
- Ein `slice` beginnt mit dem Index 0, `ar[:n]` gleicht `ar[0:n]`.
|
||||
- Der zweite Index gibt die Länge vom Slice an, wenn er ausgelassen wird. `ar[n:]` gleicht `ar[n:len(ar)]`.
|
||||
- Du kannst auch `ar[:]` nutzen, um einen gesamtes Array zu kopieren, wie in den ersten beiden Punkten erklärt.
|
||||
- Du kannst auch `ar[:]` nutzen, um einen gesamtes Array zu kopieren, wie in den ersten beiden Punkten erklärt.
|
||||
|
||||
Mehr Beispiele zu `slice`
|
||||
|
||||
@@ -383,7 +383,7 @@ Schauen wir uns ein wenig Code an. Die 'set'- und 'get'-Werte in `map` sind der
|
||||
// Ein alternativer Weg zum Deklarieren
|
||||
nummern := make(map[string]int)
|
||||
nummern["eins"] = 1 // Weise ein Wert durch einen Schlüssel zu
|
||||
nummern["zehn"] = 10
|
||||
nummern["zehn"] = 10
|
||||
nummern["drei"] = 3
|
||||
|
||||
fmt.Println("Die dritte Nummer lautet: ", nummern["drei"]) // Lese den Wert aus
|
||||
@@ -398,7 +398,7 @@ Einige Anmerkungen zur Nutzung von maps.
|
||||
|
||||
Du kannst das Schema `Schlüssel:Wert` nutzen, um eine `map` mit Startwerten zu erstellen. `map` hat auch eingebaute Funktionen, um die Existenz eines `key` zu überprüfen.
|
||||
|
||||
Benutze `delete`, um ein Element in `map` zu löschen.
|
||||
Benutze `delete`, um ein Element in `map` zu löschen.
|
||||
|
||||
// Erstelle eine map
|
||||
bewertung := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
|
||||
@@ -450,9 +450,9 @@ Standardwerte besitzen einen Wert. Dies sind die gebräuchlichsten Anwendungsfä
|
||||
float64 0 // Die Größe beträgt 8 Byte
|
||||
bool false
|
||||
string ""
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
- [Inhaltsverzeichnis](preface.md)
|
||||
- Vorheriger Abschnitt: ["Hallo Go"](02.1.md)
|
||||
- Nächster Abschnitt: [Kontrollstrukturen und Funktionen](02.3.md)
|
||||
- Nächster Abschnitt: [Kontrollstrukturen und Funktionen](02.3.md)
|
||||
|
||||
@@ -26,7 +26,7 @@ Goではどのようにこの効果的な防御を行なっているのでしょ
|
||||
図4.3 Javascriptフィルターによる出力
|
||||
|
||||
Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。しかし時にはこの`<script>alert()</script>`を正常な情報として出力したい場合があるかもしれません。そのような場合はどのように処理するべきでしょうか?この場合はtext/templateをご利用ください。下の例をご覧ください:
|
||||
|
||||
<!-- {% raw %} -->
|
||||
import "text/template"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
@@ -66,3 +66,4 @@ Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルタ
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [入力値の検証](<04.2.md>)
|
||||
* 次へ: [フォームの複数回送信の防止](<04.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 4.5 ファイルのアップロード処理
|
||||
|
||||
ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか?
|
||||
@@ -115,7 +116,7 @@
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
if err != nil {
|
||||
@@ -155,3 +156,4 @@
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [フォームの多重送信の防止](<04.4.md>)
|
||||
* 次へ: [まとめ](<04.6.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 6.4 sessionハイジャックの予防
|
||||
sessionハイジャックは広範囲に存在する比較的重大な脆弱性です。session技術において、クライアントサイドとサーバサイドはsessionのIDによってセッションを維持します。しかしこのIDは簡単にスニッフィングされ、第三者に利用されてしまいます。これは中間者攻撃の一種です。
|
||||
|
||||
@@ -87,3 +88,4 @@ sessionが始まると、生成されたsessionIDの時間を記録する一つ
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [sessionストレージ](<06.3.md>)
|
||||
* 次へ: [まとめ](<06.5.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 7.1 XMLの処理
|
||||
XMLはデータと情報のやりとりするための形式として十分普及しています。Webサービスが日々広範囲で応用されてくるにつれ、現在XMLは日常的な開発作業において重要な役割を演じてきました。この節ではGo言語の標準パッケージにあるXML関連のパッケージをご紹介します。
|
||||
|
||||
@@ -219,3 +220,4 @@ XMLをstructに解析する際は以下のルールに従います:
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [テキスト処理](<07.0.md>)
|
||||
* 次へ: [Jsonの処理](<07.2.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 7.4 テンプレートの処理
|
||||
## テンプレートとは何か
|
||||
おそらくあなたはMVCのデザインパターンについて聞いたことがあると思います。Modelはデータを処理を、Viewは表示結果を、Controllerはユーザのリクエストの制御を行います。Viewレイヤーの処理では、多くの動的な言語ではどれも静的なHTMLの中に動的言語が生成したデータを挿入します。例えばJSPでは`<%=....=%>`を挿入することで、PHPでは`<?php.....?>`を挿入することで実現します。
|
||||
@@ -348,3 +349,4 @@ Webアプリケーションを作る時はテンプレートの一部が固定
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [正規表現の処理](<07.3.md>)
|
||||
* 次へ: [ファイルの操作](<07.5.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 9.1 CSRF攻撃の予防
|
||||
|
||||
## CSRFとは何か
|
||||
@@ -91,3 +92,4 @@ tokenを検証
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [セキュリティと暗号化](<09.0.md>)
|
||||
* 次へ: [入力フィルタリングの確保](<09.2.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 10.2 ローカライズリソース
|
||||
前の節ではどのようにしてLocaleを設定するかご紹介しました。Localeを設定したあとはどのようにしてLocaleに対応する情報を保存するかという問題を解決する必要があります。ここでの情報とは以下の内容を含みます:テキスト情報、時間と日時、通貨の値、画像、ファイルや動画といったリソース等です。ここではこれらの情報に対してご紹介していきたいと思います。Go言語ではこれらのフォーマットの情報をJSONに保存します。その後それぞれ適した方法によって表示します。(以下では日本語と英語の2つの言語を対比して例を挙げます。保存の形式はそれぞれen.jsonとja-JP.jsonです。)
|
||||
## ローカライズテキスト情報
|
||||
@@ -132,3 +133,4 @@ Localeの違いによってビューを表示させる場合もあるかもし
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [デフォルトロケールの設定](<10.1.md>)
|
||||
* 次へ: [国際化サイト](<10.3.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 10.3 国際化サイト
|
||||
前の節でどのようにしてローカライズリソースを処理するかご紹介しました。Localeに対応した設定ファイルです。ではもし複数のローカライズリソースを処理する場合は?いくつかの我々が通常使用する例は:簡単なテキスト翻訳、時間や日時、数字といったものはどのように処理するのでしょうか?この節では一つ一つこれらの問題を解決していきます。
|
||||
## 複数のロケールパッケージの管理
|
||||
@@ -178,3 +179,4 @@
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [ローカライズリソース](<10.2.md>)
|
||||
* 次へ: [まとめ](<10.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
18
ja/12.2.md
18
ja/12.2.md
@@ -1,8 +1,9 @@
|
||||
<!-- {% raw %} -->
|
||||
# 12.2 サイトエラー処理
|
||||
我々のWebアプリケーションが一旦実運用されると、さまざまなエラーが発生する可能性があります。Webアプリケーションの日常の実行ではいくつものエラーが発生する可能性があります。具体的には以下のとおり:
|
||||
|
||||
- データベースエラー:データベースサーバへのアクセスまたはデータと関係のあるエラーです。例えば、以下は何らかのデータベースエラーを発生させることがあります。
|
||||
|
||||
|
||||
- 接続エラー:このエラーはデータベースサーバのネットワークが切断された時や、ユーザ名とパスワードが不正だった場合、またはデータベースが存在しない場合に発生することがあります。
|
||||
- 検索エラー:使用されたSQLが正しく無く、エラーが発生する場合です。このようなSQLエラーはもしプログラムに厳格なテストを行うことで回避できます。
|
||||
- データエラー:データベースの約束が衝突する場合。例えば一つしかないフィールドに複数の主キーを持つデータが挿入されるとエラーを発生させます。しかし、あなたのアプリケーションプログラムが運用される前に厳格なテストを行うことでこれらの問題を回避することもできます。
|
||||
@@ -27,9 +28,9 @@
|
||||
エラー処理は実は我々も第十一章の第一節でどのようにエラー処理を設計するかご紹介しました。ここではまたひとつの例から詳細にご解説します。どのように異なるエラーを処理するのでしょうか:
|
||||
|
||||
- ユーザにエラーが発生したことを通知する:
|
||||
|
||||
|
||||
ユーザがページにアクセスした時はふたつのエラーがあります:404.htmlとerror.htmlです。以下はそれぞれエラーページを表示するソースです:
|
||||
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
@@ -51,7 +52,7 @@
|
||||
</body>
|
||||
</html>
|
||||
もうひとつのソース:
|
||||
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
@@ -72,9 +73,9 @@
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
404のエラー処理ロジック、もしシステムのエラーだった場合もにたような操作になります。以下を見てみましょう:
|
||||
|
||||
|
||||
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
sayhelloName(w, r)
|
||||
@@ -90,7 +91,7 @@
|
||||
ErrorInfo := "ファイルが見つかりません" //現在のユーザ情報を取得
|
||||
t.Execute(w, ErrorInfo) //テンプレートのmerger操作を実行
|
||||
}
|
||||
|
||||
|
||||
func SystemError(w http.ResponseWriter, r *http.Request) {
|
||||
log.Critical("システムエラー") //システムエラーはクリティカルですので、ログに記録するだけでなくメールを送信します。
|
||||
t, _ = t.ParseFiles("tmpl/error.html", nil) //テンプレートファイルを解析
|
||||
@@ -109,7 +110,7 @@
|
||||
username = ""
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
username = User[uid]
|
||||
return
|
||||
}
|
||||
@@ -122,3 +123,4 @@
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [アプリケーションログ](<12.1.md>)
|
||||
* 次へ: [アプリケーションのデプロイ](<12.3.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
42
ja/13.3.md
42
ja/13.3.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 13.3 controller設計
|
||||
|
||||
伝統的なMVCフレームワークにおいて、多くの場合Action設計のサフィックス反映にもとづいています、しかしながら、現在webではREST風のフレームワークが流行しています。なるべくFilterかrewriteを使用してURLのリライトを行い、REST風のURLを実現しています。しかしなぜ直接新しくREST風のMVCフレームワークを設計しないのでしょうか?本章ではこういった考え方に基いてどのようにREST風のMVCフレームワークにフルスクラッチでcontroller、最大限に簡素化されたWebアプリケーションの開発、ひいては一行で可能な"Hello, world"の実装についてご説明します。
|
||||
@@ -17,7 +18,7 @@ MVC設計は現在Webアプリケーションの開発において最もよく
|
||||
Layout []string
|
||||
TplExt string
|
||||
}
|
||||
|
||||
|
||||
type ControllerInterface interface {
|
||||
Init(ct *Context, cn string) //コンテキストとサブクラスの名前を初期化
|
||||
Prepare() //実行前のいくつかの処理を開始
|
||||
@@ -31,7 +32,7 @@ MVC設計は現在Webアプリケーションの開発において最もよく
|
||||
Finish() //実行完了後の処理
|
||||
Render() error //methodが対応する方法を実行し終えた後、ページを構築
|
||||
}
|
||||
|
||||
|
||||
前にadd関数へのルータをご紹介した際ControllerInterfaceクラスを定義しました。ですので、ここではこのインターフェースを実装すれば十分です。基底クラスのContorollerの実装は以下のようなメソッドになります:
|
||||
|
||||
func (c *Controller) Init(ct *Context, cn string) {
|
||||
@@ -42,43 +43,43 @@ MVC設計は現在Webアプリケーションの開発において最もよく
|
||||
c.Ct = ct
|
||||
c.TplExt = "tpl"
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Prepare() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Finish() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Get() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Post() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Delete() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Put() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Head() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Patch() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Options() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Render() error {
|
||||
if len(c.Layout) > 0 {
|
||||
var filenames []string
|
||||
@@ -108,10 +109,10 @@ MVC設計は現在Webアプリケーションの開発において最もよく
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Redirect(url string, code int) {
|
||||
c.Ct.Redirect(code, url)
|
||||
}
|
||||
}
|
||||
|
||||
上のcontroller基底クラスはインターフェースが定義する関数を実装しています。urlにもとづいてルータが対応するcontrollerを実行する原則に従って、以下のように実行されます:
|
||||
|
||||
@@ -125,21 +126,21 @@ MVC設計は現在Webアプリケーションの開発において最もよく
|
||||
上ではbeegoフレームワークにおいてcontroller基底クラスの設計を完成させました。我々のアプリケーションでは我々のメソッドを以下のように設計することができます:
|
||||
|
||||
package controllers
|
||||
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
|
||||
type MainController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["Username"] = "astaxie"
|
||||
this.Data["Email"] = "astaxie@gmail.com"
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
上のメソッドではサブクラスMainControllerを実装し、Getメソッドを実装しています。もしユーザがその他のメソッド(POST/HEAD等)によってこのリソースにアクセスすると、403を返します。もしGetであれば、AutoRender=trueを設定していますのでGetメソッドの実行後自動的にRender関数が実行され、以下のようなインターフェースが表示されます:
|
||||
|
||||

|
||||
@@ -161,3 +162,4 @@ index.tplのコードは以下のようになります。データの設定と
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [カスタム定義のルータの設計](<13.2.md>)
|
||||
* 次へ: [ログとコンフィグ設計](<13.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
50
ja/13.5.md
50
ja/13.5.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 13.5 ブログの追加/削除/修正の実装
|
||||
|
||||
前ではbeegoフレームワークの全体的な構造思想の実装とニセコードの一部の実装についてご紹介しました。この節ではbeegoを通してブログシステムを設計しましょう。これにはブログの閲覧、追加、修正、削除といった操作が含まれます。
|
||||
@@ -52,19 +53,19 @@ IndexController:
|
||||
type IndexController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *IndexController) Get() {
|
||||
this.Data["blogs"] = models.GetAll()
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
ViewController:
|
||||
|
||||
type ViewController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *ViewController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
this.Data["Post"] = models.GetBlog(id)
|
||||
@@ -77,12 +78,12 @@ NewController
|
||||
type NewController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *NewController) Get() {
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "new.tpl"
|
||||
}
|
||||
|
||||
|
||||
func (this *NewController) Post() {
|
||||
inputs := this.Input()
|
||||
var blog models.Blog
|
||||
@@ -98,14 +99,14 @@ EditController
|
||||
type EditController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *EditController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
this.Data["Post"] = models.GetBlog(id)
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "edit.tpl"
|
||||
}
|
||||
|
||||
|
||||
func (this *EditController) Post() {
|
||||
inputs := this.Input()
|
||||
var blog models.Blog
|
||||
@@ -116,39 +117,39 @@ EditController
|
||||
models.SaveBlog(blog)
|
||||
this.Ctx.Redirect(302, "/")
|
||||
}
|
||||
|
||||
|
||||
DeleteController
|
||||
|
||||
type DeleteController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *DeleteController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
blog := models.GetBlog(id)
|
||||
this.Data["Post"] = blog
|
||||
models.DelBlog(blog)
|
||||
this.Ctx.Redirect(302, "/")
|
||||
}
|
||||
}
|
||||
|
||||
## modelレイヤ
|
||||
|
||||
package models
|
||||
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/astaxie/beedb"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
type Blog struct {
|
||||
Id int `PK`
|
||||
Title string
|
||||
Content string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
|
||||
func GetLink() beedb.Model {
|
||||
db, err := sql.Open("mymysql", "blog/astaxie/123456")
|
||||
if err != nil {
|
||||
@@ -157,25 +158,25 @@ DeleteController
|
||||
orm := beedb.New(db)
|
||||
return orm
|
||||
}
|
||||
|
||||
|
||||
func GetAll() (blogs []Blog) {
|
||||
db := GetLink()
|
||||
db.FindAll(&blogs)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func GetBlog(id int) (blog Blog) {
|
||||
db := GetLink()
|
||||
db.Where("id=?", id).Find(&blog)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func SaveBlog(blog Blog) (bg Blog) {
|
||||
db := GetLink()
|
||||
db.Save(&blog)
|
||||
return bg
|
||||
}
|
||||
|
||||
|
||||
func DelBlog(blog Blog) {
|
||||
db := GetLink()
|
||||
db.Delete(&blog)
|
||||
@@ -198,17 +199,17 @@ layout.tpl
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<ul id="menu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/new">New Post</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
{{.LayoutContent}}
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
index.tpl
|
||||
|
||||
<h1>Blog posts</h1>
|
||||
@@ -216,7 +217,7 @@ index.tpl
|
||||
<ul>
|
||||
{{range .blogs}}
|
||||
<li>
|
||||
<a href="/view/{{.Id}}">{{.Title}}</a>
|
||||
<a href="/view/{{.Id}}">{{.Title}}</a>
|
||||
from {{.Created}}
|
||||
<a href="/edit/{{.Id}}">Edit</a>
|
||||
<a href="/delete/{{.Id}}">Delete</a>
|
||||
@@ -228,7 +229,7 @@ view.tpl
|
||||
|
||||
<h1>{{.Post.Title}}</h1>
|
||||
{{.Post.Created}}<br/>
|
||||
|
||||
|
||||
{{.Post.Content}}
|
||||
|
||||
new.tpl
|
||||
@@ -241,7 +242,7 @@ new.tpl
|
||||
</form>
|
||||
|
||||
edit.tpl
|
||||
|
||||
|
||||
<h1>Edit {{.Post.Title}}</h1>
|
||||
|
||||
<h1>New Blog Post</h1>
|
||||
@@ -256,3 +257,4 @@ edit.tpl
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [ログとコンフィグ設計](<13.4.md>)
|
||||
* 次へ: [まとめ](<13.6.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
14
ja/14.3.md
14
ja/14.3.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 14.3 フォームおよび検証のサポート
|
||||
Web開発ではこのようなプロセスをよく見かけます:
|
||||
|
||||
@@ -28,7 +29,7 @@ Web開発ではこのようなプロセスをよく見かけます:
|
||||
Email string `form:text,valid:required|valid_email`
|
||||
Introduce string `form:textarea`
|
||||
}
|
||||
|
||||
|
||||
structを定義したらcontrollerにおいてこのように操作します
|
||||
|
||||
func (this *AddController) Get() {
|
||||
@@ -36,26 +37,26 @@ structを定義したらcontrollerにおいてこのように操作します
|
||||
this.Layout = "admin/layout.html"
|
||||
this.TplNames = "admin/add.tpl"
|
||||
}
|
||||
|
||||
|
||||
テンプレートで以下のようにフォームを表示します
|
||||
|
||||
<h1>New Blog Post</h1>
|
||||
<form action="" method="post">
|
||||
{{.form.render()}}
|
||||
</form>
|
||||
|
||||
|
||||
上では全体の第1ステップを定義しました。structからフォームを表示したあとは、ユーザが情報を入力し、サーバがデータを受け取って検証を行った後、データベースに保存されます。
|
||||
|
||||
func (this *AddController) Post() {
|
||||
var user User
|
||||
form := this.GetInput(&user)
|
||||
if !form.Validates() {
|
||||
return
|
||||
return
|
||||
}
|
||||
models.UserInsert(&user)
|
||||
this.Ctx.Redirect(302, "/admin/index")
|
||||
}
|
||||
|
||||
|
||||
## フォームの型
|
||||
以下のリストは対応するform要素の情報を表しています:
|
||||
<table cellpadding="0" cellspacing="1" border="0" style="width:100%" class="tableborder">
|
||||
@@ -121,7 +122,7 @@ structを定義したらcontrollerにおいてこのように操作します
|
||||
|
||||
</tbody></table>
|
||||
|
||||
|
||||
|
||||
## フォームの検証
|
||||
以下のリストは使用されるオリジナルのルールを表しています
|
||||
<table cellpadding="0" cellspacing="1" border="0" style="width:100%" class="tableborder">
|
||||
@@ -279,3 +280,4 @@ structを定義したらcontrollerにおいてこのように操作します
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [Sessionのサポート](<14.2.md>)
|
||||
* 次へ: [ユーザの認証](<14.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
36
ja/14.5.md
36
ja/14.5.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 14.5 多言語サポート
|
||||
第10章において国際化とローカライゼーションおよびgo-i18nライブラリの開発についてご紹介しました。この節ではこのライブラリをbeegoフレームワークの中にもってくることで、我々のフレームワークにおいて国際化とローカライゼーションをサポートさせます。
|
||||
|
||||
@@ -21,7 +22,7 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
beegoTplFuncMap["Trans"] = i18n.I18nT
|
||||
beegoTplFuncMap["TransDate"] = i18n.I18nTimeDate
|
||||
beegoTplFuncMap["TransMoney"] = i18n.I18nMoney
|
||||
|
||||
|
||||
func I18nT(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -33,7 +34,7 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
}
|
||||
return beego.Translation.Translate(s)
|
||||
}
|
||||
|
||||
|
||||
func I18nTimeDate(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -44,8 +45,8 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
s = fmt.Sprint(args...)
|
||||
}
|
||||
return beego.Translation.Time(s)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func I18nMoney(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -60,7 +61,7 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
|
||||
## 多言語開発の使用
|
||||
1. 言語および言語パッケージのパスを設定します。その後i18nオブジェクトを初期化します:
|
||||
|
||||
|
||||
beego.Lang = "zh"
|
||||
beego.LangPath = "views/lang"
|
||||
beego.InitLang()
|
||||
@@ -68,18 +69,18 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
2. 多言語パッケージの設計
|
||||
|
||||
上ではどのようにして多言語パッケージを初期化するかについてご紹介しました。今から多言語パッケージを設計します。多言語パッケージはjsonファイルです。第10章でご紹介したのと同じように、設計する必要のあるファイルをLangPathの下に置きます。例えばzh.jsonまたはen.jsonといったものです。
|
||||
|
||||
|
||||
# zh.json
|
||||
|
||||
|
||||
{
|
||||
"zh": {
|
||||
"submit": "送信",
|
||||
"create": "新規作成"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#en.json
|
||||
|
||||
|
||||
{
|
||||
"en": {
|
||||
"submit": "Submit",
|
||||
@@ -90,24 +91,25 @@ beegoにおいて以下のようにグローバル変数を設定します:
|
||||
3. 多言語パッケージを使用する
|
||||
|
||||
controllerの中でコンパイラをコールして対応する翻訳言語を取得することができます。以下に示します:
|
||||
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["create"] = beego.Translation.Translate("create")
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
テンプレートの中で直接対応する翻訳関数をコールしてもかまいません:
|
||||
|
||||
|
||||
//直接テキスト翻訳
|
||||
{{.create | Trans}}
|
||||
|
||||
|
||||
//時間の翻訳
|
||||
{{.time | TransDate}}
|
||||
|
||||
{{.time | TransDate}}
|
||||
|
||||
//通貨の翻訳
|
||||
{{.money | TransMoney}}
|
||||
|
||||
{{.money | TransMoney}}
|
||||
|
||||
## links
|
||||
* [目次](<preface.md>)
|
||||
* 前へ: [ユーザ認証](<14.4.md>)
|
||||
* 次へ: [pprofのサポート](<14.6.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../ebook/build.go
|
||||
118
ja/build.go
Normal file
118
ja/build.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fairlyblank/md2min"
|
||||
)
|
||||
|
||||
// 定义一个访问者结构体
|
||||
type Visitor struct{}
|
||||
|
||||
func (self *Visitor) md2html(arg map[string]string) error {
|
||||
from := arg["from"]
|
||||
to := arg["to"]
|
||||
err := filepath.Walk(from+"/", func(path string, f os.FileInfo, err error) error {
|
||||
if f == nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if (f.Mode() & os.ModeSymlink) > 0 {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(f.Name(), ".md") {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
input_byte, _ := ioutil.ReadAll(file)
|
||||
input := string(input_byte)
|
||||
input = regexp.MustCompile(`\[(.*?)\]\(<?(.*?)\.md>?\)`).ReplaceAllString(input, "[$1](<$2.html>)")
|
||||
|
||||
if f.Name() == "README.md" {
|
||||
input = regexp.MustCompile(`https:\/\/github\.com\/astaxie\/build-web-application-with-golang\/blob\/master\/`).ReplaceAllString(input, "")
|
||||
}
|
||||
|
||||
// 以#开头的行,在#后增加空格
|
||||
// 以#开头的行, 删除多余的空格
|
||||
input = FixHeader(input)
|
||||
|
||||
// 删除页面链接
|
||||
input = RemoveFooterLink(input)
|
||||
|
||||
// remove image suffix
|
||||
input = RemoveImageLinkSuffix(input)
|
||||
|
||||
var out *os.File
|
||||
filename := strings.Replace(f.Name(), ".md", ".html", -1)
|
||||
fmt.Println(to + "/" + filename)
|
||||
if out, err = os.Create(to + "/" + filename); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating %s: %v", f.Name(), err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer out.Close()
|
||||
md := md2min.New("none")
|
||||
err = md.Parse([]byte(input), out)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Parsing Error", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func FixHeader(input string) string {
|
||||
re_header := regexp.MustCompile(`(?m)^#.+$`)
|
||||
re_sub := regexp.MustCompile(`^(#+)\s*(.+)$`)
|
||||
fixer := func(header string) string {
|
||||
s := re_sub.FindStringSubmatch(header)
|
||||
return s[1] + " " + s[2]
|
||||
}
|
||||
return re_header.ReplaceAllStringFunc(input, fixer)
|
||||
}
|
||||
|
||||
func RemoveFooterLink(input string) string {
|
||||
re_footer := regexp.MustCompile(`(?m)^#{2,} links.*?\n(.+\n)*`)
|
||||
return re_footer.ReplaceAllString(input, "")
|
||||
}
|
||||
|
||||
func RemoveImageLinkSuffix(input string) string {
|
||||
re_footer := regexp.MustCompile(`png\?raw=true`)
|
||||
return re_footer.ReplaceAllString(input, "png")
|
||||
}
|
||||
|
||||
func main() {
|
||||
tmp := os.Getenv("TMP")
|
||||
if tmp == "" {
|
||||
tmp = "."
|
||||
}
|
||||
|
||||
workdir := os.Getenv("WORKDIR")
|
||||
if workdir == "" {
|
||||
workdir = "."
|
||||
}
|
||||
|
||||
arg := map[string]string{
|
||||
"from": workdir,
|
||||
"to": tmp,
|
||||
}
|
||||
|
||||
v := &Visitor{}
|
||||
err := v.md2html(arg)
|
||||
if err != nil {
|
||||
fmt.Printf("filepath.Walk() returned %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../ebook/build.sh
|
||||
56
ja/build.sh
Executable file
56
ja/build.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
SED='sed'
|
||||
|
||||
if [ `uname -s` == 'Darwin' ] ; then
|
||||
SED='gsed'
|
||||
fi
|
||||
|
||||
bn="`basename $0`"
|
||||
WORKDIR="$(cd $(dirname $0); pwd -P)"
|
||||
|
||||
#
|
||||
# Default language: zh
|
||||
# You can overwrite following variables in config file.
|
||||
#
|
||||
MSG_INSTALL_PANDOC_FIRST='请先安装pandoc,然后再次运行'
|
||||
MSG_SUCCESSFULLY_GENERATED='build-web-application-with-golang.epub 已经建立'
|
||||
MSG_CREATOR='Astaxie'
|
||||
MSG_DESCRIPTION='一本开源的Go Web编程书籍'
|
||||
MSG_LANGUAGE='zh-CN'
|
||||
MSG_TITLE='Go Web编程'
|
||||
[ -e "$WORKDIR/config" ] && . "$WORKDIR/config"
|
||||
|
||||
|
||||
TMP=`mktemp -d 2>/dev/null || mktemp -d -t "${bn}"` || exit 1
|
||||
trap 'rm -rf "$TMP"' 0 1 2 3 15
|
||||
|
||||
|
||||
cd "$TMP"
|
||||
|
||||
(
|
||||
[ go list github.com/fairlyblank/md2min >/dev/null 2>&1 ] || export GOPATH="$PWD"
|
||||
go get -u github.com/fairlyblank/md2min
|
||||
WORKDIR="$WORKDIR" TMP="$TMP" go run "$WORKDIR/build.go"
|
||||
)
|
||||
|
||||
if [ ! type -P pandoc >/dev/null 2>&1 ]; then
|
||||
echo "$MSG_INSTALL_PANDOC_FIRST"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat <<__METADATA__ > metadata.txt
|
||||
<dc:creator>$MSG_CREATOR</dc:creator>
|
||||
<dc:description>$MSG_DESCRIPTION</dc:description>
|
||||
<dc:language>$MSG_LANGUAGE</dc:language>
|
||||
<dc:rights>Creative Commons</dc:rights>
|
||||
<dc:title>$MSG_TITLE</dc:title>
|
||||
__METADATA__
|
||||
|
||||
mkdir -p $TMP/images
|
||||
cp -r $WORKDIR/images/* $TMP/images/
|
||||
ls [0-9]*.html | xargs $SED -i "s/png?raw=true/png/g"
|
||||
|
||||
pandoc --reference-links -S --toc -f html -t epub --epub-metadata=metadata.txt --epub-cover-image="$WORKDIR/images/cover.png" -o "$WORKDIR/../build-web-application-with-golang.epub" `ls [0-9]*.html | sort`
|
||||
|
||||
echo "$MSG_SUCCESSFULLY_GENERATED"
|
||||
@@ -1,6 +1,7 @@
|
||||
# 9.1 预防CSRF攻击
|
||||
|
||||
## 什么是CSRF
|
||||
|
||||
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
|
||||
|
||||
那么CSRF到底能够干嘛呢?你可以这样简单的理解:攻击者可以盗用你的登陆信息,以你的身份模拟发送各种请求。攻击者只要借助少许的社会工程学的诡计,例如通过QQ等聊天软件发送的链接(有些还伪装成短域名,用户无法分辨),攻击者就能迫使Web应用的用户去执行攻击者预设的操作。例如,当用户登录网络银行去查看其存款余额,在他没有退出时,就点击了一个QQ好友发来的链接,那么该用户银行帐户中的资金就有可能被转移到攻击者指定的帐户中。
|
||||
@@ -8,6 +9,7 @@ CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也
|
||||
所以遇到CSRF攻击时,将对终端用户的数据和操作指令构成严重的威胁;当受攻击的终端用户具有管理员帐户的时候,CSRF攻击将危及整个Web应用程序。
|
||||
|
||||
## CSRF的原理
|
||||
|
||||
下图简单阐述了CSRF攻击的思想
|
||||
|
||||

|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 10.2 本地化资源
|
||||
前面小节我们介绍了如何设置Locale,设置好Locale之后我们需要解决的问题就是如何存储相应的Locale对应的信息呢?这里面的信息包括:文本信息、时间和日期、货币值、图片、包含文件以及视图等资源。那么接下来我们将对这些信息一一进行介绍,Go语言中我们把这些格式信息存储在JSON中,然后通过合适的方式展现出来。(接下来以中文和英文两种语言对比举例,存储格式文件en.json和zh-CN.json)
|
||||
## 本地化文本消息
|
||||
@@ -132,3 +133,4 @@ $GOROOT/lib/time包中的timeinfo.zip含有locale对应的时区的定义,为
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [设置默认地区](<10.1.md>)
|
||||
* 下一节: [国际化站点](<10.3.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 10.3 国际化站点
|
||||
前面小节介绍了如何处理本地化资源,即Locale一个相应的配置文件,那么如果处理多个的本地化资源呢?而对于一些我们经常用到的例如:简单的文本翻译、时间日期、数字等如果处理呢?本小节将一一解决这些问题。
|
||||
## 管理多个本地包
|
||||
@@ -178,3 +179,4 @@
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [本地化资源](<10.2.md>)
|
||||
* 下一节: [小结](<10.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
18
zh/12.2.md
18
zh/12.2.md
@@ -1,8 +1,9 @@
|
||||
<!-- {% raw %} -->
|
||||
# 12.2 网站错误处理
|
||||
我们的Web应用一旦上线之后,那么各种错误出现的概率都有,Web应用日常运行中可能出现多种错误,具体如下所示:
|
||||
|
||||
- 数据库错误:指与访问数据库服务器或数据相关的错误。例如,以下可能出现的一些数据库错误。
|
||||
|
||||
|
||||
- 连接错误:这一类错误可能是数据库服务器网络断开、用户名密码不正确、或者数据库不存在。
|
||||
- 查询错误:使用的SQL非法导致错误,这样子SQL错误如果程序经过严格的测试应该可以避免。
|
||||
- 数据错误:数据库中的约束冲突,例如一个唯一字段中插入一条重复主键的值就会报错,但是如果你的应用程序在上线之前经过了严格的测试也是可以避免这类问题。
|
||||
@@ -27,9 +28,9 @@
|
||||
错误处理其实我们已经在十一章第一小节里面有过介绍如何设计错误处理,这里我们再从一个例子详细的讲解一下,如何来处理不同的错误:
|
||||
|
||||
- 通知用户出现错误:
|
||||
|
||||
|
||||
通知用户在访问页面的时候我们可以有两种错误:404.html和error.html,下面分别显示了错误页面的源码:
|
||||
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
@@ -51,7 +52,7 @@
|
||||
</body>
|
||||
</html>
|
||||
另一个源码:
|
||||
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
@@ -72,9 +73,9 @@
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
404的错误处理逻辑,如果是系统的错误也是类似的操作,同时我们看到在:
|
||||
|
||||
|
||||
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
sayhelloName(w, r)
|
||||
@@ -90,7 +91,7 @@
|
||||
ErrorInfo := "文件找不到" //获取当前用户信息
|
||||
t.Execute(w, ErrorInfo) //执行模板的merger操作
|
||||
}
|
||||
|
||||
|
||||
func SystemError(w http.ResponseWriter, r *http.Request) {
|
||||
log.Critical("系统错误") //系统错误触发了Critical,那么不仅会记录日志还会发送邮件
|
||||
t, _ = t.ParseFiles("tmpl/error.html", nil) //解析模板文件
|
||||
@@ -109,7 +110,7 @@
|
||||
username = ""
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
username = User[uid]
|
||||
return
|
||||
}
|
||||
@@ -122,3 +123,4 @@
|
||||
* [目录](<preface.md>)
|
||||
* 上一章: [应用日志](<12.1.md>)
|
||||
* 下一节: [应用部署](<12.3.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
42
zh/13.3.md
42
zh/13.3.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 13.3 controller设计
|
||||
|
||||
传统的MVC框架大多数是基于Action设计的后缀式映射,然而,现在Web流行REST风格的架构。尽管使用Filter或者rewrite能够通过URL重写实现REST风格的URL,但是为什么不直接设计一个全新的REST风格的 MVC框架呢?本小节就是基于这种思路来讲述如何从头设计一个基于REST风格的MVC框架中的controller,最大限度地简化Web应用的开发,甚至编写一行代码就可以实现“Hello, world”。
|
||||
@@ -17,7 +18,7 @@ MVC设计模式是目前Web应用开发中最常见的架构模式,通过分
|
||||
Layout []string
|
||||
TplExt string
|
||||
}
|
||||
|
||||
|
||||
type ControllerInterface interface {
|
||||
Init(ct *Context, cn string) //初始化上下文和子类名称
|
||||
Prepare() //开始执行之前的一些处理
|
||||
@@ -31,7 +32,7 @@ MVC设计模式是目前Web应用开发中最常见的架构模式,通过分
|
||||
Finish() //执行完成之后的处理
|
||||
Render() error //执行完method对应的方法之后渲染页面
|
||||
}
|
||||
|
||||
|
||||
那么前面介绍的路由add函数的时候是定义了ControllerInterface类型,因此,只要我们实现这个接口就可以,所以我们的基类Controller实现如下的方法:
|
||||
|
||||
func (c *Controller) Init(ct *Context, cn string) {
|
||||
@@ -42,43 +43,43 @@ MVC设计模式是目前Web应用开发中最常见的架构模式,通过分
|
||||
c.Ct = ct
|
||||
c.TplExt = "tpl"
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Prepare() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Finish() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Get() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Post() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Delete() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Put() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Head() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Patch() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Options() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Render() error {
|
||||
if len(c.Layout) > 0 {
|
||||
var filenames []string
|
||||
@@ -108,10 +109,10 @@ MVC设计模式是目前Web应用开发中最常见的架构模式,通过分
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) Redirect(url string, code int) {
|
||||
c.Ct.Redirect(code, url)
|
||||
}
|
||||
}
|
||||
|
||||
上面的controller基类已经实现了接口定义的函数,通过路由根据url执行相应的controller的原则,会依次执行如下:
|
||||
|
||||
@@ -125,21 +126,21 @@ MVC设计模式是目前Web应用开发中最常见的架构模式,通过分
|
||||
上面beego框架中完成了controller基类的设计,那么我们在我们的应用中可以这样来设计我们的方法:
|
||||
|
||||
package controllers
|
||||
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
|
||||
type MainController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["Username"] = "astaxie"
|
||||
this.Data["Email"] = "astaxie@gmail.com"
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
上面的方式我们实现了子类MainController,实现了Get方法,那么如果用户通过其他的方式(POST/HEAD等)来访问该资源都将返回403,而如果是Get来访问,因为我们设置了AutoRender=true,那么在执行完Get方法之后会自动执行Render函数,就会显示如下界面:
|
||||
|
||||

|
||||
@@ -161,3 +162,4 @@ index.tpl的代码如下所示,我们可以看到数据的设置和显示都
|
||||
* [目录](<preface.md>)
|
||||
* 上一章: [自定义路由器设计](<13.2.md>)
|
||||
* 下一节: [日志和配置设计](<13.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
50
zh/13.5.md
50
zh/13.5.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 13.5 实现博客的增删改
|
||||
|
||||
前面介绍了beego框架实现的整体构思以及部分实现的伪代码,这小节介绍通过beego建立一个博客系统,包括博客浏览、添加、修改、删除等操作。
|
||||
@@ -53,19 +54,19 @@ IndexController:
|
||||
type IndexController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *IndexController) Get() {
|
||||
this.Data["blogs"] = models.GetAll()
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
ViewController:
|
||||
|
||||
type ViewController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *ViewController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
this.Data["Post"] = models.GetBlog(id)
|
||||
@@ -78,12 +79,12 @@ NewController
|
||||
type NewController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *NewController) Get() {
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "new.tpl"
|
||||
}
|
||||
|
||||
|
||||
func (this *NewController) Post() {
|
||||
inputs := this.Input()
|
||||
var blog models.Blog
|
||||
@@ -99,14 +100,14 @@ EditController
|
||||
type EditController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *EditController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
this.Data["Post"] = models.GetBlog(id)
|
||||
this.Layout = "layout.tpl"
|
||||
this.TplNames = "edit.tpl"
|
||||
}
|
||||
|
||||
|
||||
func (this *EditController) Post() {
|
||||
inputs := this.Input()
|
||||
var blog models.Blog
|
||||
@@ -117,39 +118,39 @@ EditController
|
||||
models.SaveBlog(blog)
|
||||
this.Ctx.Redirect(302, "/")
|
||||
}
|
||||
|
||||
|
||||
DeleteController
|
||||
|
||||
type DeleteController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
|
||||
func (this *DeleteController) Get() {
|
||||
id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
|
||||
blog := models.GetBlog(id)
|
||||
this.Data["Post"] = blog
|
||||
models.DelBlog(blog)
|
||||
this.Ctx.Redirect(302, "/")
|
||||
}
|
||||
}
|
||||
|
||||
## model层
|
||||
|
||||
package models
|
||||
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/astaxie/beedb"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
type Blog struct {
|
||||
Id int `PK`
|
||||
Title string
|
||||
Content string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
|
||||
func GetLink() beedb.Model {
|
||||
db, err := sql.Open("mymysql", "blog/astaxie/123456")
|
||||
if err != nil {
|
||||
@@ -158,25 +159,25 @@ DeleteController
|
||||
orm := beedb.New(db)
|
||||
return orm
|
||||
}
|
||||
|
||||
|
||||
func GetAll() (blogs []Blog) {
|
||||
db := GetLink()
|
||||
db.FindAll(&blogs)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func GetBlog(id int) (blog Blog) {
|
||||
db := GetLink()
|
||||
db.Where("id=?", id).Find(&blog)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func SaveBlog(blog Blog) (bg Blog) {
|
||||
db := GetLink()
|
||||
db.Save(&blog)
|
||||
return bg
|
||||
}
|
||||
|
||||
|
||||
func DelBlog(blog Blog) {
|
||||
db := GetLink()
|
||||
db.Delete(&blog)
|
||||
@@ -199,17 +200,17 @@ layout.tpl
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<ul id="menu">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/new">New Post</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
{{.LayoutContent}}
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
index.tpl
|
||||
|
||||
<h1>Blog posts</h1>
|
||||
@@ -217,7 +218,7 @@ index.tpl
|
||||
<ul>
|
||||
{{range .blogs}}
|
||||
<li>
|
||||
<a href="/view/{{.Id}}">{{.Title}}</a>
|
||||
<a href="/view/{{.Id}}">{{.Title}}</a>
|
||||
from {{.Created}}
|
||||
<a href="/edit/{{.Id}}">Edit</a>
|
||||
<a href="/delete/{{.Id}}">Delete</a>
|
||||
@@ -229,7 +230,7 @@ view.tpl
|
||||
|
||||
<h1>{{.Post.Title}}</h1>
|
||||
{{.Post.Created}}<br/>
|
||||
|
||||
|
||||
{{.Post.Content}}
|
||||
|
||||
new.tpl
|
||||
@@ -242,7 +243,7 @@ new.tpl
|
||||
</form>
|
||||
|
||||
edit.tpl
|
||||
|
||||
|
||||
<h1>Edit {{.Post.Title}}</h1>
|
||||
|
||||
<h1>New Blog Post</h1>
|
||||
@@ -257,3 +258,4 @@ edit.tpl
|
||||
* [目录](<preface.md>)
|
||||
* 上一章: [日志和配置设计](<13.4.md>)
|
||||
* 下一节: [小结](<13.6.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
16
zh/14.3.md
16
zh/14.3.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 14.3 表单及验证支持
|
||||
在Web开发中对于这样的一个流程可能很眼熟:
|
||||
|
||||
@@ -28,7 +29,7 @@
|
||||
Email string `form:text,valid:required|valid_email`
|
||||
Introduce string `form:textarea`
|
||||
}
|
||||
|
||||
|
||||
定义好struct之后接下来在controller中这样操作
|
||||
|
||||
func (this *AddController) Get() {
|
||||
@@ -36,26 +37,26 @@
|
||||
this.Layout = "admin/layout.html"
|
||||
this.TplNames = "admin/add.tpl"
|
||||
}
|
||||
|
||||
|
||||
在模板中这样显示表单
|
||||
|
||||
<h1>New Blog Post</h1>
|
||||
<form action="" method="post">
|
||||
{{.form.render()}}
|
||||
</form>
|
||||
|
||||
|
||||
上面我们定义好了整个的第一步,从struct到显示表单的过程,接下来就是用户填写信息,服务器端接收数据然后验证,最后插入数据库。
|
||||
|
||||
func (this *AddController) Post() {
|
||||
var user User
|
||||
form := this.GetInput(&user)
|
||||
if !form.Validates() {
|
||||
return
|
||||
return
|
||||
}
|
||||
models.UserInsert(&user)
|
||||
this.Ctx.Redirect(302, "/admin/index")
|
||||
}
|
||||
|
||||
|
||||
## 表单类型
|
||||
以下列表列出来了对应的form元素信息:
|
||||
<table cellpadding="0" cellspacing="1" border="0" style="width:100%" class="tableborder">
|
||||
@@ -121,7 +122,7 @@
|
||||
|
||||
</tbody></table>
|
||||
|
||||
|
||||
|
||||
## 表单验证
|
||||
以下列表将列出可被使用的原生规则
|
||||
<table cellpadding="0" cellspacing="1" border="0" style="width:100%" class="tableborder">
|
||||
@@ -278,4 +279,5 @@
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [Session支持](<14.2.md>)
|
||||
* 下一节: [用户认证](<14.4.md>)
|
||||
* 下一节: [用户认证](<14.4.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
38
zh/14.5.md
38
zh/14.5.md
@@ -1,3 +1,4 @@
|
||||
<!-- {% raw %} -->
|
||||
# 14.5 多语言支持
|
||||
我们在第十章介绍过国际化和本地化,开发了一个go-i18n库,这小节我们将把该库集成到beego框架里面来,使得我们的框架支持国际化和本地化。
|
||||
|
||||
@@ -21,7 +22,7 @@ beego中设置全局变量如下:
|
||||
beegoTplFuncMap["Trans"] = i18n.I18nT
|
||||
beegoTplFuncMap["TransDate"] = i18n.I18nTimeDate
|
||||
beegoTplFuncMap["TransMoney"] = i18n.I18nMoney
|
||||
|
||||
|
||||
func I18nT(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -33,7 +34,7 @@ beego中设置全局变量如下:
|
||||
}
|
||||
return beego.Translation.Translate(s)
|
||||
}
|
||||
|
||||
|
||||
func I18nTimeDate(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -44,8 +45,8 @@ beego中设置全局变量如下:
|
||||
s = fmt.Sprint(args...)
|
||||
}
|
||||
return beego.Translation.Time(s)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func I18nMoney(args ...interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
@@ -60,7 +61,7 @@ beego中设置全局变量如下:
|
||||
|
||||
## 多语言开发使用
|
||||
1. 设置语言以及语言包所在位置,然后初始化i18n对象:
|
||||
|
||||
|
||||
beego.Lang = "zh"
|
||||
beego.LangPath = "views/lang"
|
||||
beego.InitLang()
|
||||
@@ -68,18 +69,18 @@ beego中设置全局变量如下:
|
||||
2. 设计多语言包
|
||||
|
||||
上面讲了如何初始化多语言包,现在设计多语言包,多语言包是json文件,如第十章介绍的一样,我们需要把设计的文件放在LangPath下面,例如zh.json或者en.json
|
||||
|
||||
|
||||
# zh.json
|
||||
|
||||
|
||||
{
|
||||
"zh": {
|
||||
"submit": "提交",
|
||||
"create": "创建"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#en.json
|
||||
|
||||
|
||||
{
|
||||
"en": {
|
||||
"submit": "Submit",
|
||||
@@ -90,24 +91,25 @@ beego中设置全局变量如下:
|
||||
3. 使用语言包
|
||||
|
||||
我们可以在controller中调用翻译获取响应的翻译语言,如下所示:
|
||||
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["create"] = beego.Translation.Translate("create")
|
||||
this.TplNames = "index.tpl"
|
||||
}
|
||||
|
||||
|
||||
我们也可以在模板中直接调用响应的翻译函数:
|
||||
|
||||
|
||||
//直接文本翻译
|
||||
{{.create | Trans}}
|
||||
|
||||
|
||||
//时间翻译
|
||||
{{.time | TransDate}}
|
||||
|
||||
{{.time | TransDate}}
|
||||
|
||||
//货币翻译
|
||||
{{.money | TransMoney}}
|
||||
|
||||
{{.money | TransMoney}}
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [用户认证](<14.4.md>)
|
||||
* 下一节: [pprof支持](<14.6.md>)
|
||||
* 下一节: [pprof支持](<14.6.md>)
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
Reference in New Issue
Block a user