From c4e06abcf2a3feb947c35d4e87b52de9b75e06e4 Mon Sep 17 00:00:00 2001 From: Noval Agung Prayogo Date: Sat, 27 Apr 2024 22:32:55 +0700 Subject: [PATCH] feat: Writing improvement section B (#269) * feat: improve content * feat: KBBI stuff * feat: update B1 * feat: wording improvement, mainly on section B --- content/A-array.md | 2 +- content/A-buffered-channel.md | 2 +- content/A-channel-range-close.md | 2 +- content/A-channel-select.md | 2 +- content/A-channel-timeout.md | 2 +- content/A-channel.md | 2 +- content/A-client-http-request-simple.md | 2 +- content/A-command-line-args-flag.md | 2 +- content/A-concurrency-pipeline.md | 10 +-- content/A-data-type-conversion.md | 2 +- content/A-defer-exit.md | 2 +- content/A-encoding-base64.md | 2 +- content/A-error-panic-recover.md | 2 +- content/A-exec.md | 2 +- content/A-file.md | 2 +- content/A-fungsi-closure.md | 2 +- content/A-fungsi-multiple-return.md | 2 +- content/A-fungsi-sebagai-parameter.md | 2 +- content/A-fungsi-variadic.md | 2 +- content/A-fungsi.md | 2 +- content/A-go-vendoring.md | 2 +- content/A-golang-generics.md | 4 +- content/A-goroutine.md | 2 +- content/A-hash-sha1.md | 2 +- content/A-hello-world.md | 2 +- content/A-interface-kosong.md | 2 +- content/A-interface.md | 2 +- content/A-json.md | 2 +- content/A-komentar.md | 2 +- content/A-konstanta.md | 2 +- content/A-map.md | 2 +- content/A-method.md | 2 +- content/A-mongodb.md | 2 +- content/A-mutex.md | 2 +- content/A-operator.md | 2 +- content/A-perulangan.md | 2 +- content/A-pipeline-context-cancellation.md | 4 +- content/A-pointer.md | 2 +- content/A-properti-public-dan-private.md | 2 +- content/A-random.md | 2 +- content/A-reflect.md | 2 +- content/A-regex.md | 2 +- content/A-seleksi-kondisi.md | 2 +- .../A-simplified-fan-in-fan-out-pipeline.md | 4 +- content/A-slice.md | 4 +- content/A-sql.md | 2 +- content/A-string-format.md | 2 +- content/A-strings.md | 2 +- content/A-struct.md | 2 +- content/A-time-duration.md | 2 +- content/A-time-parsing-format.md | 2 +- content/A-timer-ticker-scheduler.md | 4 +- content/A-tipe-data.md | 2 +- content/A-unit-test.md | 2 +- content/A-url-parsing.md | 2 +- content/A-variabel.md | 2 +- content/A-waitgroup.md | 2 +- content/A-web-server.md | 2 +- content/A-web-service-api.md | 2 +- content/B-ajax-json-payload.md | 39 ++++++------ content/B-ajax-json-response.md | 22 +++---- content/B-ajax-multi-upload.md | 46 +++++++------- content/B-cookie.md | 31 +++++----- content/B-custom-mux-multiplexer.md | 32 +++++----- content/B-download-file.md | 25 ++++---- content/B-form-upload-file.md | 30 ++++----- content/B-form-value.md | 22 +++---- content/B-golang-web-hello-world.md | 40 ++++++------ content/B-http-basic-auth.md | 62 ++++++++++--------- content/B-http-method-basic.md | 25 ++++---- content/B-middleware-using-http-handler.md | 58 +++++++++-------- content/B-render-html-string.md | 12 ++-- content/B-render-specific-html-template.md | 17 ++--- content/B-routing-http-handlefunc.md | 21 +++---- content/B-routing-static-assets.md | 30 ++++----- ...erver-handler-http-request-cancellation.md | 40 ++++++------ content/B-simple-configuration.md | 53 ++++++++-------- content/B-template-actions-variables.md | 18 +++--- content/B-template-custom-functions.md | 16 ++--- content/B-template-functions.md | 8 ++- content/B-template-render-html.md | 16 ++--- content/B-template-render-partial-html.md | 16 ++--- content/C-advanced-configuration-viper.md | 2 +- content/C-advanced-middleware-and-logging.md | 2 +- .../C-best-practice-configuration-env-var.md | 2 +- content/C-client-http-request-advanced.md | 2 +- content/C-convert-html-to-pdf.md | 2 +- content/C-cors-preflight-request.md | 2 +- content/C-csrf.md | 2 +- content/C-dockerize-golang.md | 6 +- content/C-echo-routing.md | 2 +- content/C-echo-template-rendering.md | 4 +- content/C-flag-parser.md | 2 +- content/C-golang-aws-s3.md | 2 +- content/C-golang-ftp.md | 2 +- content/C-golang-grpc-protobuf.md | 2 +- content/C-golang-jwt.md | 2 +- content/C-golang-ldap-authentication.md | 2 +- content/C-golang-protobuf-implementation.md | 2 +- content/C-golang-redis.md | 2 +- content/C-golang-ssh-sftp.md | 2 +- content/C-golang-sso-saml-sp.md | 4 +- content/C-http-error-handling.md | 2 +- content/C-http-gzip-compression.md | 2 +- content/C-http-request-payload-validation.md | 2 +- content/C-http2-server-push.md | 2 +- content/C-https-tls.md | 4 +- .../C-parsing-http-request-payload-echo.md | 2 +- content/C-read-write-excel-xlsx-file.md | 2 +- content/C-scraping-parsing-html.md | 2 +- .../C-secure-insecure-client-http-request.md | 2 +- content/C-secure-middleware.md | 2 +- content/C-securecookie.md | 2 +- content/C-send-email.md | 2 +- content/C-session.md | 2 +- content/C-singleflight.md | 2 +- content/C-write-pdf-file.md | 2 +- content/C-xml-parser.md | 2 +- content/D-golang-web-socket-chatting-app.md | 2 +- content/D-google-api-search.md | 2 +- ...ert-1mil-csv-record-into-db-in-a-minute.md | 4 +- content/README.md | 2 +- 122 files changed, 465 insertions(+), 442 deletions(-) diff --git a/content/A-array.md b/content/A-array.md index 2d4e75f32..cfb047bb9 100644 --- a/content/A-array.md +++ b/content/A-array.md @@ -199,7 +199,7 @@ Parameter pertama keyword `make` diisi dengan tipe data elemen array yang diingi --- diff --git a/content/A-buffered-channel.md b/content/A-buffered-channel.md index b5e16bf82..11f3f026c 100644 --- a/content/A-buffered-channel.md +++ b/content/A-buffered-channel.md @@ -68,7 +68,7 @@ Lebih detailnya mengenai fungsi `time.Sleep()` dan `time.Second` dibahas pada ch --- diff --git a/content/A-channel-range-close.md b/content/A-channel-range-close.md index 9ce0ee6a7..3bd29d9f0 100644 --- a/content/A-channel-range-close.md +++ b/content/A-channel-range-close.md @@ -73,7 +73,7 @@ Dan sebaliknya pada fungsi `printMessage(ch <-chan string)`, channel `ch` hanya --- diff --git a/content/A-channel-select.md b/content/A-channel-select.md index 07b9603bd..bd779f787 100644 --- a/content/A-channel-select.md +++ b/content/A-channel-select.md @@ -81,7 +81,7 @@ Cukup mudah bukan? --- diff --git a/content/A-channel-timeout.md b/content/A-channel-timeout.md index a76d38bae..59d151d3a 100644 --- a/content/A-channel-timeout.md +++ b/content/A-channel-timeout.md @@ -68,7 +68,7 @@ Muncul output setiap kali ada penerimaan data dengan delay waktu acak. Ketika da --- diff --git a/content/A-channel.md b/content/A-channel.md index 583ada27a..c7cedee60 100644 --- a/content/A-channel.md +++ b/content/A-channel.md @@ -164,7 +164,7 @@ fmt.Println(message) --- diff --git a/content/A-client-http-request-simple.md b/content/A-client-http-request-simple.md index 91b737d3f..89c6d64cf 100644 --- a/content/A-client-http-request-simple.md +++ b/content/A-client-http-request-simple.md @@ -173,7 +173,7 @@ Sampai sini kita telah belajar bagaimana cara membuat http request sederhana unt --- diff --git a/content/A-command-line-args-flag.md b/content/A-command-line-args-flag.md index d79470285..e93a3f24f 100644 --- a/content/A-command-line-args-flag.md +++ b/content/A-command-line-args-flag.md @@ -130,7 +130,7 @@ Kegunaan dari parameter terakhir method-method flag adalah untuk memunculkan hin --- diff --git a/content/A-concurrency-pipeline.md b/content/A-concurrency-pipeline.md index d842a7649..7d09766b1 100644 --- a/content/A-concurrency-pipeline.md +++ b/content/A-concurrency-pipeline.md @@ -8,7 +8,7 @@ Go memiliki beberapa API untuk keperluan konkurensi, dua diantaranya adalah *gor Definisi *pipeline* yang paling mudah versi penulis adalah **beberapa/banyak proses yang berjalan secara konkuren yang masing-masing proses merupakan bagian dari serangkaian tahapan proses yang berhubungan satu sama lain**. -Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang di mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut: +Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut: 1. Kita perlu data *list* dari semua database yang harus di-backup, beserta alamat akses dan kredensial-nya. 2. Kita jalankan proses backup, bisa secara sekuensial (setelah `db1` selesai, lanjut `db2`, lanjut `db3`, dst), atau secara paralel (proses backup `db1`, `db2`, `db3`, dan lainnya dijalankan secara bersamaan). @@ -383,12 +383,12 @@ Fungsi `getSum()` menerima channel dan akan secara aktif memantau dan membaca da Nah, karena di sini kita punya 3 worker yang jelasnya menghasilkan 3 buah channel baru, kita perlu sebuah mekanisme untuk menggabung channel tersebut, agar nanti mudah untuk dikontrol ([SSoT](https://en.wikipedia.org/wiki/Single_source_of_truth)). Di sinilah peran fungsi `mergeChanFileInfo()`. -Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang di mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**. +Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**. Jadi TL;DR nya: * Fungsi Fan-out digunakan untuk pembuatan worker, untuk distribusi job, yang proses distribusinya sendiri akan berhenti ketika channel inputan di-close. -* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang di mana channel baru ini juga otomatis di-close ketika channel input adalah closed. +* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang mana channel baru ini juga otomatis di-close ketika channel input adalah closed. Sekarang lanjut buat fungsi `getSum()`. @@ -408,7 +408,7 @@ func getSum(chanIn <-chan FileInfo) <-chan FileInfo { } ``` -Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang di mana channel ini merupakan nilai balik fungsi `getSum()`. +Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang mana channel ini merupakan nilai balik fungsi `getSum()`. Ketika `chanIn` closed, maka bisa diasumsikan semua data sudah dikirim. Jika memang iya dan data-data tersebut sudah di proses (pencarian md5hash-nya), maka channel `chanOut` juga di-close. @@ -539,7 +539,7 @@ Ok sekian untuk chapter panjang ini. --- diff --git a/content/A-data-type-conversion.md b/content/A-data-type-conversion.md index 135d4f3ba..d46d8af97 100644 --- a/content/A-data-type-conversion.md +++ b/content/A-data-type-conversion.md @@ -247,7 +247,7 @@ Kombinasi `switch` - `case` bisa dimanfaatkan untuk deteksi tipe konkret data ya --- diff --git a/content/A-defer-exit.md b/content/A-defer-exit.md index bb719c471..7361a2807 100644 --- a/content/A-defer-exit.md +++ b/content/A-defer-exit.md @@ -131,7 +131,7 @@ Meskipun `defer fmt.Println("halo")` ditempatkan sebelum `os.Exit()`, statement --- diff --git a/content/A-encoding-base64.md b/content/A-encoding-base64.md index 5d4bbfd1d..b0f1255ee 100644 --- a/content/A-encoding-base64.md +++ b/content/A-encoding-base64.md @@ -83,7 +83,7 @@ fmt.Println(decodedString) --- diff --git a/content/A-error-panic-recover.md b/content/A-error-panic-recover.md index dca1c4ff4..6f3ba2640 100644 --- a/content/A-error-panic-recover.md +++ b/content/A-error-panic-recover.md @@ -220,7 +220,7 @@ Bisa dilihat di dalam perulangan terdapat sebuah IIFE untuk recover panic dan ju --- diff --git a/content/A-exec.md b/content/A-exec.md index 7c05f887e..e6943b853 100644 --- a/content/A-exec.md +++ b/content/A-exec.md @@ -55,7 +55,7 @@ Selain `.Output()` ada sangat banyak sekali API untuk keperluan komunikasi denga --- diff --git a/content/A-file.md b/content/A-file.md index 58b386141..d68749919 100644 --- a/content/A-file.md +++ b/content/A-file.md @@ -154,7 +154,7 @@ func main() { --- diff --git a/content/A-fungsi-closure.md b/content/A-fungsi-closure.md index b8e6ab54b..70801b73b 100644 --- a/content/A-fungsi-closure.md +++ b/content/A-fungsi-closure.md @@ -176,7 +176,7 @@ Output program: --- diff --git a/content/A-fungsi-multiple-return.md b/content/A-fungsi-multiple-return.md index 2ac5893f0..e30d1a649 100644 --- a/content/A-fungsi-multiple-return.md +++ b/content/A-fungsi-multiple-return.md @@ -97,7 +97,7 @@ Fungsi `math.Pow()` digunakan untuk operasi pangkat nilai. `math.Pow(2, 3)` bera --- diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md index 0e27fde4d..b714c0a39 100644 --- a/content/A-fungsi-sebagai-parameter.md +++ b/content/A-fungsi-sebagai-parameter.md @@ -102,7 +102,7 @@ Variabel `result` bernilai `true` karena string `"ang"` merupakan bagian dari st --- diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md index 7263e9926..056ad76f7 100644 --- a/content/A-fungsi-variadic.md +++ b/content/A-fungsi-variadic.md @@ -147,7 +147,7 @@ Output program: --- diff --git a/content/A-fungsi.md b/content/A-fungsi.md index dc748eec4..81a66aa39 100644 --- a/content/A-fungsi.md +++ b/content/A-fungsi.md @@ -177,7 +177,7 @@ Di dalamnya terdapat proses validasi nilai variabel pembagi, jika nilainya adala --- diff --git a/content/A-go-vendoring.md b/content/A-go-vendoring.md index c2deefdb0..0489772c1 100644 --- a/content/A-go-vendoring.md +++ b/content/A-go-vendoring.md @@ -62,7 +62,7 @@ Konsekuensi penerapan vendoring adalah size project menjadi cukup besar. Untuk p --- diff --git a/content/A-golang-generics.md b/content/A-golang-generics.md index e79828b1a..07e85c9e3 100644 --- a/content/A-golang-generics.md +++ b/content/A-golang-generics.md @@ -63,7 +63,7 @@ Penulisan notasi fungsi dengan Generic kurang lebih sebagai berikut: func FuncName[dataType ](params) ``` -Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang di mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen. +Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen. ```go func Sum(numbers []int) int { @@ -276,7 +276,7 @@ Sampai artikel ini ditulis, generic tidak bisa diterapkan pada method (meski bis --- diff --git a/content/A-goroutine.md b/content/A-goroutine.md index 758b71cc0..59d598bdb 100644 --- a/content/A-goroutine.md +++ b/content/A-goroutine.md @@ -91,7 +91,7 @@ Bisa dilihat pada kode di atas, untuk menampung inputan text `trafalgar d law`, --- diff --git a/content/A-hash-sha1.md b/content/A-hash-sha1.md index 213fdce2d..bbea5df35 100644 --- a/content/A-hash-sha1.md +++ b/content/A-hash-sha1.md @@ -97,7 +97,7 @@ Metode ini sering dipakai untuk enkripsi password user. Salt dan data hasil hash --- diff --git a/content/A-hello-world.md b/content/A-hello-world.md index df9f388b5..f1e954de0 100644 --- a/content/A-hello-world.md +++ b/content/A-hello-world.md @@ -125,7 +125,7 @@ Contoh statement di atas akan menghasilkan output: **Hello world! how are you**. --- diff --git a/content/A-interface-kosong.md b/content/A-interface-kosong.md index ca9e8edbb..af7fb4058 100644 --- a/content/A-interface-kosong.md +++ b/content/A-interface-kosong.md @@ -150,7 +150,7 @@ for _, each := range fruits { --- diff --git a/content/A-interface.md b/content/A-interface.md index 136d76ed0..cd484ed4a 100644 --- a/content/A-interface.md +++ b/content/A-interface.md @@ -179,7 +179,7 @@ Pada chapter [A.23. Pointer](/A-pointer.html) dijelaskan bahwa method pointer bi --- diff --git a/content/A-json.md b/content/A-json.md index d20c97510..67e505925 100644 --- a/content/A-json.md +++ b/content/A-json.md @@ -132,7 +132,7 @@ Output program: --- diff --git a/content/A-komentar.md b/content/A-komentar.md index 0790e783b..ea364f2ce 100644 --- a/content/A-komentar.md +++ b/content/A-komentar.md @@ -47,7 +47,7 @@ Sifat komentar ini sama seperti komentar inline, yaitu sama-sama diabaikan oleh --- diff --git a/content/A-konstanta.md b/content/A-konstanta.md index e3c130222..468de16e4 100644 --- a/content/A-konstanta.md +++ b/content/A-konstanta.md @@ -100,7 +100,7 @@ const three, four string = "tiga", "empat" --- diff --git a/content/A-map.md b/content/A-map.md index d8fa8aff8..9a9ab776f 100644 --- a/content/A-map.md +++ b/content/A-map.md @@ -173,7 +173,7 @@ var data = []map[string]string{ --- diff --git a/content/A-method.md b/content/A-method.md index ba0d3f47f..cdd2afb53 100644 --- a/content/A-method.md +++ b/content/A-method.md @@ -157,7 +157,7 @@ Lebih detailnya dibahas pada chapter selanjutnya. --- diff --git a/content/A-mongodb.md b/content/A-mongodb.md index d3786986c..8d277336c 100644 --- a/content/A-mongodb.md +++ b/content/A-mongodb.md @@ -327,7 +327,7 @@ if len(result) > 0 { --- diff --git a/content/A-mutex.md b/content/A-mutex.md index 66ee5c926..e4c263342 100644 --- a/content/A-mutex.md +++ b/content/A-mutex.md @@ -172,7 +172,7 @@ func main() { --- diff --git a/content/A-operator.md b/content/A-operator.md index 1c2135657..eb3402faa 100644 --- a/content/A-operator.md +++ b/content/A-operator.md @@ -93,7 +93,7 @@ Template `\t` digunakan untuk menambahkan indent tabulasi. Biasa dimanfaatkan un --- diff --git a/content/A-perulangan.md b/content/A-perulangan.md index bc0d43608..635b42628 100644 --- a/content/A-perulangan.md +++ b/content/A-perulangan.md @@ -162,7 +162,7 @@ Pada `for` bagian dalam, terdapat seleksi kondisi untuk pengecekan nilai `i`. Ke --- diff --git a/content/A-pipeline-context-cancellation.md b/content/A-pipeline-context-cancellation.md index e904ca11e..a20895b7a 100644 --- a/content/A-pipeline-context-cancellation.md +++ b/content/A-pipeline-context-cancellation.md @@ -241,7 +241,7 @@ Cara pembuatan object context sendiri sebenarnya ada 3: 3. Menggunakan fungsi `context.With...`. Fungsi ini sebenarnya bukan digunakan untuk inisialisasi objek konteks baru, tapi digunakan untuk menambahkan informasi tertentu pada *copied* context yang disisipkan di parameter pertama pemanggilan fungsi. Ada 3 buah fungsi `context.With...` yang bisa digunakan, yaitu: - Fungsi `context.WithCancel(ctx) (ctx, cancel)`. Fungsi ini digunakan untuk menambahkan fasilitas *cancellable* pada context yang disisipkan sebagai parameter pertama pemanggilan fungsi. Lewat nilai balik kedua, yaitu `cancel` yang tipenya `context.CancelFunc`, kita bisa secara paksa meng-*cancel* context ini. - - Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang di mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa. + - Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa. - Fungsi `context.WithTimeout(ctx, time.Duration) (ctx, cancel)`. Fungsi ini sama seperti `context.WithDeadline()`, bedanya pada parameter kedua argument bertipe durasi (bukan objek `time.Time`). Kesamaan dari ketiga fungsi `context.With...` adalah sama-sama menambahkan fasilitas *cancellable* yang bisa dieksekusi lewat nilai balik kedua fungsi tersebut (yang tipenya `context.CancelFunc`). @@ -448,7 +448,7 @@ Perbedannya ada pada penerapan *cancellation*-nya. Pada contoh ini kita tidak me --- diff --git a/content/A-pointer.md b/content/A-pointer.md index 53117e3b4..1d782695a 100644 --- a/content/A-pointer.md +++ b/content/A-pointer.md @@ -101,7 +101,7 @@ Nilai variabel `number` berubah menjadi `10` karena perubahan yang terjadi di da --- diff --git a/content/A-properti-public-dan-private.md b/content/A-properti-public-dan-private.md index 9382caad9..3de0f328e 100644 --- a/content/A-properti-public-dan-private.md +++ b/content/A-properti-public-dan-private.md @@ -337,7 +337,7 @@ Di Go, setiap package masing-masing boleh memiliki fungsi `init()`. Fungsi terse --- diff --git a/content/A-random.md b/content/A-random.md index 474293c6a..3f6621e79 100644 --- a/content/A-random.md +++ b/content/A-random.md @@ -117,7 +117,7 @@ Dengan fungsi di atas kita bisa dengan mudah meng-generate string random dengan --- diff --git a/content/A-reflect.md b/content/A-reflect.md index dc6bb128c..1d626f9f5 100644 --- a/content/A-reflect.md +++ b/content/A-reflect.md @@ -168,7 +168,7 @@ Jika eksekusi method diikuti pengisian parameter, maka parameternya harus dituli --- diff --git a/content/A-regex.md b/content/A-regex.md index 1503460c8..8b9db8d91 100644 --- a/content/A-regex.md +++ b/content/A-regex.md @@ -165,7 +165,7 @@ Pada contoh di atas, ekspresi regexp `[a-b]+` digunakan sebagai kriteria split. --- diff --git a/content/A-seleksi-kondisi.md b/content/A-seleksi-kondisi.md index e5906b979..b13792992 100644 --- a/content/A-seleksi-kondisi.md +++ b/content/A-seleksi-kondisi.md @@ -202,7 +202,7 @@ if point > 7 { --- diff --git a/content/A-simplified-fan-in-fan-out-pipeline.md b/content/A-simplified-fan-in-fan-out-pipeline.md index e3ecc0229..a8278f685 100644 --- a/content/A-simplified-fan-in-fan-out-pipeline.md +++ b/content/A-simplified-fan-in-fan-out-pipeline.md @@ -1,6 +1,6 @@ # A.63. Concurrency Pattern: Simplified Fan-out Fan-in Pipeline -Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang di mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren. +Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren. > Penulis sangat anjurkan untuk mencoba mempelajari praktek chapter sebelumnya terlebih dahulu jika belum. Karena chapter kali ini ada hubungannya dengan chapter tersebut. @@ -334,7 +334,7 @@ Nah dari sini semoga cukup jelas ya bedanya kalau dari sisi performa. Inilah pen --- diff --git a/content/A-slice.md b/content/A-slice.md index d459aa7df..93ffee30d 100644 --- a/content/A-slice.md +++ b/content/A-slice.md @@ -151,7 +151,7 @@ fruits[x:y] **Slicing** yang dimulai dari indeks **0** hingga **y** akan mengembalikan elemen-elemen mulai indeks **0** hingga sebelum indeks **y**, dengan lebar kapasitas adalah sama dengan slice aslinya. -Sedangkan slicing yang dimulai dari indeks **x**, yang di mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah. +Sedangkan slicing yang dimulai dari indeks **x**, yang mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah. ## A.16.6. Fungsi `append()` @@ -258,7 +258,7 @@ fmt.Println(cap(bFruits)) // cap: 2 --- diff --git a/content/A-sql.md b/content/A-sql.md index d2464aadb..2f4ceb2a5 100644 --- a/content/A-sql.md +++ b/content/A-sql.md @@ -342,7 +342,7 @@ Selengkapya mengenai driver yang tersedia di Go silakan lihat di [https://go.dev --- diff --git a/content/A-string-format.md b/content/A-string-format.md index 5e0cea832..3c66f8a09 100644 --- a/content/A-string-format.md +++ b/content/A-string-format.md @@ -270,7 +270,7 @@ fmt.Printf("%%\n") --- diff --git a/content/A-strings.md b/content/A-strings.md index 7419ae2d0..90145ea99 100644 --- a/content/A-strings.md +++ b/content/A-strings.md @@ -158,7 +158,7 @@ fmt.Println(str) // "EAT!" --- diff --git a/content/A-struct.md b/content/A-struct.md index f4f757dc7..a3fa021c7 100644 --- a/content/A-struct.md +++ b/content/A-struct.md @@ -408,7 +408,7 @@ var num Number = 12 --- diff --git a/content/A-time-duration.md b/content/A-time-duration.md index 354be56c9..2e7521d4a 100644 --- a/content/A-time-duration.md +++ b/content/A-time-duration.md @@ -121,7 +121,7 @@ duration := time.Duration(n) * time.Second --- diff --git a/content/A-time-parsing-format.md b/content/A-time-parsing-format.md index 8eb4070e1..e463af1f4 100644 --- a/content/A-time-parsing-format.md +++ b/content/A-time-parsing-format.md @@ -214,7 +214,7 @@ Kode di atas menghasilkan error karena format tidak sesuai dengan skema data yan --- diff --git a/content/A-timer-ticker-scheduler.md b/content/A-timer-ticker-scheduler.md index ddcce0eb7..92d4c4853 100644 --- a/content/A-timer-ticker-scheduler.md +++ b/content/A-timer-ticker-scheduler.md @@ -126,7 +126,7 @@ func main() { Pada contoh di atas bisa dilihat, selain ticker disiapkan juga variabel channel `done`. Variabel ini kita gunakan untuk mengontrol kapan ticker harus di stop. -Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang di mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker). +Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker). Data yang dikirimkan via channel `ticker.C` adalah data date-time kapan event itu terjadi. Pada kode di atas, setiap ada kiriman data via channel tersebut kita tampilkan. @@ -197,7 +197,7 @@ Ketika user tidak menginputkan apa-apa dalam kurun waktu 5 detik, pesan timeout --- diff --git a/content/A-tipe-data.md b/content/A-tipe-data.md index 9e72bfaaa..b8c8462f0 100644 --- a/content/A-tipe-data.md +++ b/content/A-tipe-data.md @@ -122,7 +122,7 @@ Nantinya kita akan sering bertemu dengan nilai `nil` setelah masuk pada pembahas --- diff --git a/content/A-unit-test.md b/content/A-unit-test.md index 8e427229a..03beb1ed1 100644 --- a/content/A-unit-test.md +++ b/content/A-unit-test.md @@ -187,7 +187,7 @@ Fungsi `assert.Equal()` digunakan untuk uji perbandingan. Parameter ke-2 dibandi --- diff --git a/content/A-url-parsing.md b/content/A-url-parsing.md index a12e46917..0bdc2ede6 100644 --- a/content/A-url-parsing.md +++ b/content/A-url-parsing.md @@ -39,7 +39,7 @@ Selain itu, query yang ada pada url akan otomatis diparsing juga, menjadi bentuk --- diff --git a/content/A-variabel.md b/content/A-variabel.md index 77d2fd87d..bf421eb4c 100644 --- a/content/A-variabel.md +++ b/content/A-variabel.md @@ -183,7 +183,7 @@ Nantinya kita akan bahas lebih detail ketika sudah masuk ke pembahasan masing-ma --- diff --git a/content/A-waitgroup.md b/content/A-waitgroup.md index 608acc80a..6075a62ae 100644 --- a/content/A-waitgroup.md +++ b/content/A-waitgroup.md @@ -72,7 +72,7 @@ Kombinasi yang tepat antara `sync.WaitGroup` dan channel sangat penting, keduany --- diff --git a/content/A-web-server.md b/content/A-web-server.md index 0eb5bf7cf..3a35e279a 100644 --- a/content/A-web-server.md +++ b/content/A-web-server.md @@ -125,7 +125,7 @@ Sampai chapter ini yang kita pelajari adalah yang sifatnya fundamental atau dasa --- diff --git a/content/A-web-service-api.md b/content/A-web-service-api.md index e4df9a3c1..c84a5a2fc 100644 --- a/content/A-web-service-api.md +++ b/content/A-web-service-api.md @@ -143,7 +143,7 @@ Data user ID pada endpoint `/user` ditulis dalam format query parameters, yaitu --- diff --git a/content/B-ajax-json-payload.md b/content/B-ajax-json-payload.md index 25d46d77f..4beadd093 100644 --- a/content/B-ajax-json-payload.md +++ b/content/B-ajax-json-payload.md @@ -1,22 +1,24 @@ # B.14. AJAX JSON Payload -Sebelumnya kita telah belajar bagaimana cara submit data dari front-end ke back-end menggunakan teknik **Form Data**. Kali ini kita akan belajar tentang cara request menggunakan teknik **Request Payload** dengan tipe payload adalah **JSON**. +Sebelumnya kita telah mempelajari cara submit data dari front-end ke back-end dengan menggunakan payload **Form Data**. Kali ini kita akan belajar tentang cara request menggunakan payload **JSON**. -Teknik request **Form Data** digunakan salah satu nya pada request submit lewat `
`. Pada chapter ini, kita tidak akan menggunakan cara submit lewat form, melainkan menggunakan teknik AJAX (Asynchronous JavaScript And XML), dengan payload ber-tipe JSON. +> **Form Data** merupakan tipe payload default HTTP request via tag `` -[Perbedaan](http://stackoverflow.com/a/23152367/1467988) antara kedua jenis request tersebut adalah pada isi header `Content-Type`, dan bentuk informasi dikirimkan. Secara default, request lewat ``, content type-nya adalah `application/x-www-form-urlencoded`. Data dikirimkan dalam bentuk query string (key-value) seperti `id=n001&nama=bruce`. +Pada chapter ini, kita tidak akan menggunakan tag `` untuk submit data, melainkan dengan memanfaatkan teknik AJAX (Asynchronous JavaScript And XML) dengan payload JSON. -> Ketika di form ditambahkan atribut `enctype="multipart/form-data"`, maka content type berubah menjadi `multipart/form-data`. +Sebenarnya [perbedaan](http://stackoverflow.com/a/23152367/1467988) antara kedua jenis request tersebut ada di dua hal, yaitu isi header `Content-Type` dan struktur informasi dikirimkan. Request lewat `` secara default memiliki content type `application/x-www-form-urlencoded`, efeknya data dikirimkan dalam bentuk query string (key-value) seperti `id=n001&nama=bruce`. -Request Payload JSON sedikit berbeda, `Content-Type` berisikan `application/json`, dan data disisipkan dalam `Body` dalam bentuk **JSON** string. +> Pengiriman data via tag `` sebenarnya bisa menggunakan content-type selain `application/x-www-form-urlencoded`, yaitu `multipart/form-data`. + +Untuk payload JSON, `Content-Type` yang digunakan adalah `application/json`. Dengannya, data disisipkan di dalam `Body` request dalam bentuk **JSON** string. ## B.14.1. Struktur Folder Proyek -OK, langsung saja, pertama siapkan proyek dengan struktur seperti pada gambar di bawah ini. +OK, mari praktek. Pertama siapkan proyek dengan struktur seperti gambar berikut. ![Struktur proyek](images/B_ajax_json_payload_1_structure.png) -> Silakan unduh file js jQuery dari situs official-nya. +> Silakan unduh file JS jQuery dari situs official-nya. ## B.14.2. Front End - HTML @@ -43,7 +45,7 @@ Layout dari view perlu disiapkan terlebih dahulu, tulis kode berikut pada file ` ``` -Selanjutnya, pada tag `` tambahkan tabel sederhana berisikan inputan-inputan yang diperlukan. Ada tiga buah inputan yang harus dipersiapkan, yaitu: *Name*, *Age*, dan *Gender*; dan juga sebuah button untuk submit form. +Selanjutnya, pada tag `` tambahkan tabel sederhana dengan isi didalamnya adalah inputan form. Ada tiga buah inputan yang perlu dibuat yaitu: *Name*, *Age*, dan *Gender*. Selain itu, sebuah button untuk keperluan submit form juga perlu disiapkan. ```html @@ -79,7 +81,7 @@ Selanjutnya, pada tag `` tambahkan tabel sederhana berisikan inputan-inp ## B.14.3. Front End - HTML -Sekarang kita masuk ke bagian paling menyenangkan/menyebalkan (tergantung taste), yaitu javascript. Siapkan sebuah event `submit` pada `#user-form`. Dalam event tersebut default handler event submit milik `` di-override, diganti dengan AJAX request. +Sekarang kita masuk ke bagian paling menyenangkan/menyebalkan (tergantung taste), yaitu javascript. Siapkan sebuah event `submit` pada `#user-form`. Default handler untuk event submit milik `` di-override, diganti dengan AJAX request. ```js $("#user-form").on("submit", function (e) { @@ -105,9 +107,9 @@ $("#user-form").on("submit", function (e) { }); ``` -Value semua inputan diambil lalu dimasukkan dalam sebuah objek lalu di stringify (agar menjadi JSON string), untuk kemudian di jadikan sebagai payload request. Bisa dilihat pada kode AJAX di atas, `contentType` nilainya adalah `application/json`. +Value semua inputan dalam form diambil, kemudian dimasukkan ke sebuah objek lalu di stringify, agar berubah menjadi JSON string untuk kemudian di jadikan sebagai payload request. Bisa dilihat pada kode AJAX di atas, `contentType` nilainya adalah `application/json`. -Respon dari ajax di atas akan dimunculkan pada `

`. +Respon dari AJAX di atas nantinya dimunculkan pada `

`. ## B.14.4. Back End @@ -145,7 +147,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } ``` -Sedangkan `handleSave` akan memproses request yang di-submit dari bagian depan. +Sedangkan `handleSave` akan memproses request yang di-submit dari front-end. ```go func handleSave(w http.ResponseWriter, r *http.Request) { @@ -175,24 +177,25 @@ func handleSave(w http.ResponseWriter, r *http.Request) { } ``` -Isi payload didapatkan dengan cara men-decode body request (`r.Body`). Proses decoding tidak dilakukan menggunakan `json.Unmarshal()` melainkan lewat json decoder, karena akan [lebih efisien](http://stackoverflow.com/a/21198571/1467988) untuk jenis kasus seperti ini. +Isi payload didapatkan dengan cara men-decode body request (`r.Body`). Proses decoding tidak dilakukan menggunakan `json.Unmarshal()` melainkan lewat JSON decoder dengan alasan [efisiensinya lebih baik](http://stackoverflow.com/a/21198571/1467988). -> Gunakan `json.Decoder` jika data adalah stream `io.Reader`. Gunakan `json.Unmarshal()` untuk decode data sumbernya sudah ada di memory. +- `json.Decoder` cocok digunakan untuk decode data JSON yang sumber datanya adalah stream `io.Reader`, contohnya seperti `r.Body`. +- `json.Unmarshal()` cocok untuk proses decoding yang sumber datanya sudah tersimpan di variabel (bukan stream). -## B.14.5. Test +## B.14.5. Testing -Jalankan program, test hasilnya di browser. +Jalankan program yang telah dibuat, test hasilnya di browser. ![Hasil tes](images/B_ajax_json_payload_2_test.png) -Gunakan fasilitas Developer Tools pada Chrome untuk melihat detail dari request. +Gunakan fasilitas Developer Tools pada Chrome untuk menginspeksi aktifitas AJAX-nya. ![Request](images/B_ajax_json_payload_3_inspect.png) --- diff --git a/content/B-ajax-json-response.md b/content/B-ajax-json-response.md index 127c6d6ef..23bad5207 100644 --- a/content/B-ajax-json-response.md +++ b/content/B-ajax-json-response.md @@ -1,6 +1,6 @@ # B.15. AJAX JSON Response -Pada chapter sebelumnya, kita belajar cara untuk memproses request dengan payload bertipe JSON. Pada chapter ini kita akan belajar untuk membuat satu endpoint yang mengembalikan data JSON string. +Kita telah belajar cara untuk memproses request dengan payload bertipe JSON di chapter sebelumnya. Pembelajaran kali ini masih tentang tipe data JSON tapi lebih fokus ke bagian back-end-nya, yaitu membuat sebuah Web Service API sederhana yang mengembalikan response berbentuk JSON. ## B.15.1. Praktek @@ -21,7 +21,7 @@ func main() { } ``` -Selanjutnya buat handler untuk rute `/`. Di dalam fungsi ini, data dummy ber-tipe slice object disiapkan. Data ini akan dikonversi ke JSON lalu dijadikan nilai balik endpoint `/`. +Selanjutnya buat handler untuk rute `/`. Di dalam fungsi tersenit, disiapkan data dummy ber-tipe slice object. Data ini kemudian dikonversi ke JSON lalu dijadikan nilai balik endpoint `/`. ```go func ActionIndex(w http.ResponseWriter, r *http.Request) { @@ -46,29 +46,27 @@ func ActionIndex(w http.ResponseWriter, r *http.Request) { } ``` -Cara mengkonversi data ke bentuk json cukup mudah, bisa menggunakan `json.Marshal()`. Fungsi ini mengembalikan dua nilai balik, data json (dalam bentuk `[]byte`) dan error jika ada. +Cara mengkonversi data ke bentuk json cukup mudah, bisa menggunakan `json.Marshal()`. Fungsi ini mengembalikan dua nilai balik, data JSON dalam bentuk `[]byte`, dan error jika ada. > Untuk mengambil bentuk string dari hasil konversi JSON, cukup lakukan casting pada data slice bytes tersebut. Contoh: `string(jsonInBytes)` -Karena nilai balik konversi sudah dalam bentuk bytes, maka langsung saja panggil method `Write()` milik `http.ResponseWriter` dan sisipkan data json sebagai argument pemanggilan method. +Karena nilai balik konversi sudah dalam bentuk bytes, maka langsung saja panggil method `Write()` milik `http.ResponseWriter` untuk menjadikannya sebagai API response. Panggil method tersebut, kemudian sisipkan data JSON sebagai argument pemanggilan method. Jangan lupa juga untuk menambahkan response header `Content-Type: application/json`. ## B.15.2. Testing -OK, semua sudah selesai, lakukan testing. +OK, semua sudah selesai, jalankan program lalu test API-nya. ![Testing web server](images/B_ajax_json_response_1_test.png) ## B.15.3. JSON Response menggunakan JSON.Encoder -Pada chapter sebelumnya sudah disinggung, bahwa lebih baik menggunakan `json.Decoder` jika ingin men-decode data yang sumbernya ada di stream `io.Reader` +Pada chapter sebelumnya telah disinggung bahwa lebih baik menggunakan `json.Decoder` jika ingin men-decode data yang sumbernya ada di stream `io.Reader` -Package json juga memiliki fungsi lain-nya yaitu `json.Encoder`, yang sangat cocok digunakan untuk meng-encode data menjadi JSON dengan tujuan objek langsung ke stream `io.Reader`. +Selain `json.Decoder`, ada juga `json.Encoder` yang penggunaannya adalah untuk meng-encode data menjadi JSON dengan output langsung disimpan ke stream `io.Reader`. -Karena tipe `http.ResponseWriter` adalah meng-embed `io.Reader`, maka jelasnya bisa kita terapkan penggunaan encoder di sini. - -Contohnya penerapannya sebagai berikut. +Tipe `http.ResponseWriter` adalah meng-embed `io.Reader`, maka tipe tersebut bisa kita gunakan pada proses encoding menggunakan `json.Encoder`. Contoh penerapannya bisa dilihat berikut ini. ```go w.Header().Set("Content-Type", "application/json") @@ -80,12 +78,12 @@ if err != nil { } ``` -Kode di atas hasilnya ekuivalen dengan penggunaan `json.Marshal`. +Kode di atas hasilnya ekuivalen dengan encoding data object ke JSON string menggunakan `json.Marshal()`. --- diff --git a/content/B-ajax-multi-upload.md b/content/B-ajax-multi-upload.md index d6a5fcc2f..2fb47ca14 100644 --- a/content/B-ajax-multi-upload.md +++ b/content/B-ajax-multi-upload.md @@ -1,20 +1,20 @@ # B.16. AJAX Multiple File Upload -Pada chapter ini, kita akan belajar 3 hal dalam satu waktu, yaitu: +Pada chapter ini, kita akan belajar 3 hal sekaligus yang mencakup poin-poin berikut: -1. Bagaiamana cara untuk upload file via AJAX. -2. Cara untuk handle upload banyak file sekaligus. -3. Cara handle upload file yang lebih hemat memori. +1. Cara untuk upload file via AJAX +2. Cara untuk handle upload banyak file sekaligus +3. Cara handle upload file yang lebih hemat memori -Sebelumnya pada chapter [B.13. Form Upload File](/B-form-upload-file.html), pemrosesan file upload dilakukan lewat **ParseMultipartForm**, sedangkan pada chapter ini metode yang dipakai berbeda, yaitu menggunakan **MultipartReader**. +Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), pemrosesan file upload dilakukan lewat **ParseMultipartForm**, sedangkan pada chapter ini metode yang dipakai berbeda, yaitu **MultipartReader**. -Kelebihan dari `MultipartReader` adalah, file yang di upload **tidak** di simpan sebagai file temporary di lokal terlebih dahulu (tidak seperti `ParseMultipartForm`), melainkan langsung diambil dari stream `io.Reader`. +Kelebihan dari `MultipartReader` adalah, file yang di upload **tidak** di simpan pada file temporary di lokal terlebih dahulu (tidak seperti `ParseMultipartForm`), melainkan data file bisa diambil langsung dari stream `io.Reader`. -Di bagian front end, upload file secara asynchronous bisa dilakukan menggunakan objek [FormData](https://developer.mozilla.org/en/docs/Web/API/FormData). Semua file dimasukkan dalam objek `FormData`, setelah itu objek tersebut dijadikan sebagai payload AJAX request. +Cara penerapan `MultipartReader` ini membutuhkan front-end untuk melakukan upload file secara asynchronous menggunakan objek [FormData](https://developer.mozilla.org/en/docs/Web/API/FormData). Semua file yang akan di-upload diambil konten dan metadatanya menggunakan javascript untuk dimasukkan ke objek `FormData`. Setelahnya, object tersebut dijadikan sebagai payload AJAX request. ## B.16.1. Struktur Folder Proyek -Mari langsung kita praktekkan, pertama siapkan proyek dengan struktur seperti gambar di bawah ini. +Mari praktekkan, pertama siapkan proyek dengan struktur seperti gambar di bawah ini. ![Folder Structure](images/B_ajax_multi_upload_1_structure.png) @@ -22,9 +22,9 @@ Mari langsung kita praktekkan, pertama siapkan proyek dengan struktur seperti ga ## B.16.2. Front End -Buka `view.html`, siapkan template dasar view. Dalam file ini terdapat satu buah inputan upload file yang mendukung multi upload, dan satu buah tombol submit. +Buka `view.html`, siapkan template dasar view. Dalam file ini terdapat satu buah inputan upload file yang mendukung multi-upload, dan satu buah tombol submit. -Untuk meng-enable kapabilitas multi upload, cukup tambahkan atribut `multiple` pada input file. +Untuk mengaktifkan kapabilitas multi upload, cukup tambahkan atribut `multiple` pada input file. ```html @@ -77,18 +77,18 @@ $("#user-form").on("submit", function (e) { }); ``` -Objek inputan files (yang didapat dari `$("#upload-file")[0].files`) memiliki property `.files` yang isinya merupakan array dari semua file yang dipilih oleh user ketika upload. File-file tersebut di-loop, dimasukkan ke dalam objek `FormData` yang telah dibuat. +Objek inputan files (yang didapat dari `$("#upload-file")[0]`) memiliki property `.files` yang isinya merupakan array dari semua file yang dipilih oleh user ketika upload. File-file tersebut diiterasi, setiap datanya dimasukkan ke dalam objek `FormData` yang telah dibuat. -AJAX dilakukan lewat `jQuery.ajax`. Berikut adalah penjelasan mengenai konfigurasi `processData` dan `contentType` dalam AJAX yang sudah dibuat. +Operasi AJAX request dilakukan lewat `jQuery.ajax`. Berikut adalah penjelasan mengenai konfigurasi `processData` dan `contentType` dalam AJAX yang sudah dibuat. - Konfigurasi `contentType` perlu di set ke `false` agar header Content-Type yang dikirim bisa menyesuaikan data yang disisipkan. - Konfigurasi `processData` juga perlu di set ke `false`, agar data yang akan di kirim tidak otomatis dikonversi ke query string atau json string (tergantung `contentType`). Pada konteks ini kita memerlukan payload tetap dalam tipe `FormData`. -## B.16.3. Back End +## B.16.3. Back-End -Ada 2 route handler yang harus dipersiapkan di back end. Pertama adalah rute `/` yang menampilkan form upload, dan rute `/upload` untuk pemrosesan upload sendiri. +Ada 2 route handler yang harus dipersiapkan di back-end. Pertama adalah rute `/` untuk keperluan memunculkan form upload, dan rute `/upload` untuk pemrosesan upload dari AJAX request. -Buka file `main.go`, isi dengan package yang dibutuhkan, lalu lakukan registrasi dua rute yang dimaksud di atas, beserta satu buah rute untuk static assets. +Buka file `main.go`, import package yang diperlukan, lalu deklarasikan dua rute yang telah disebut di atas, beserta satu buah rute baru untuk *serving* static assets. ```go package main @@ -123,11 +123,11 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } ``` -Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), metode yang digunakan untuk handle file upload adalah menggunakan `ParseMultipartForm`, file di proses dalam memori dengan alokasi tertentu, dan jika melebihi alokasi maka akan disimpan pada temporary file. +Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), metode yang digunakan untuk handle file upload adalah `ParseMultipartForm`. Cara kerjanya, file di proses dalam memori dengan alokasi tertentu, dan jika melebihi alokasi maka akan disimpan pada temporary file. Metode tersebut kurang tepat guna jika digunakan untuk memproses file yang ukurannya besar (file size melebihi `maxMemory`) atau jumlah file-nya sangat banyak (memakan waktu, karena isi dari masing-masing file akan ditampung pada file *temporary* sebelum benar-benar di-copy ke file tujuan). -Solusinya dari dua masalah di atas adalah menggunakan `MultipartReader` untuk handling file upload. Dengan metode ini, file destinasi isinya akan di-copy lagsung dari stream `io.Reader`, tanpa butuh file temporary untuk perantara. +Solusi dari dua masalah yang telah disebutkan adalah menggunakan `MultipartReader` untuk handling file upload. Lewat metode ini, file destinasi isidi-copy lagsung dari stream `io.Reader` tanpa butuh file temporary untuk perantara. Kembali ke bagian perkodingan, siapkan fungsi `handleUpload`, isinya kode berikut. @@ -149,9 +149,9 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { } ``` -Bisa dilihat, method `.MultipartReader()` dipanggil dari objek request milik handler. Mengembalikan dua objek, pertama `*multipart.Reader` dan `error` (jika ada). +Bisa dilihat, method `.MultipartReader()` dipanggil dari objek request milik handler. Operasi tersebut menghasilkan dua nilai balik, `*multipart.Reader` dan `error` (jika ada). -Selanjutnya lakukan perulangan terhadap objek `reader`. Setiap file yang di-upload di proses di masing-masing perulangan. Setelah looping berakhir. idealnya semua file sudah terproses dengan benar. +Selanjutnya, lakukan perulangan terhadap objek `reader`. Setiap file yang di-upload di proses di masing-masing perulangan. Setelah looping berakhir, idealnya semua file sudah terproses dengan benar. ```go for { @@ -181,11 +181,11 @@ w.Write([]byte(`all files uploaded`)) Method `.NextPart()` mengembalikan 2 informasi, yaitu objek stream `io.Reader` (dari file yg di upload), dan `error`. -File destinasi dipersiapkan, kemudian diisi dengan data dari stream file, menggunakan `io.Copy()`. +File destinasi disiapkan kemudian diisi dengan data dari stream file, menggunakan `io.Copy()`. -Jika `reader.NextPart()` mengembalikan error `io.EOF`, menandakan bahwa semua file sudah di proses, maka hentikan perulangan. +Jika `reader.NextPart()` mengembalikan error `io.EOF`, maka bisa disimpulkan semua file telah di proses, kemudian perulangan dihentikan. -OK, semua persiapan sudah cukup. +OK, semua persiapan sudah cukup, selanjutnya masuk fase testing. ## B.16.4. Testing @@ -200,7 +200,7 @@ Cek apakah file sudah terupload. --- diff --git a/content/B-cookie.md b/content/B-cookie.md index 7b8fa6eb5..e718ab1bc 100644 --- a/content/B-cookie.md +++ b/content/B-cookie.md @@ -1,10 +1,10 @@ # B.21. HTTP Cookie -Cookie adalah data dalam bentuk teks yang disimpan pada komputer (oleh web browser) ketika pengunjung sedang surfing ke sebuah situs. Cookie dapat dibuat dari sisi front end (javascript) maupun back end (dalam konteks ini Go). +Cookie adalah data berbentuk teks yang disimpan pada komputer (oleh web browser) yang biasanya didapat ketika pengunjung sedang surfing di internet, mengakses situs website. Pembuatan cookie dilakukan di sisi aplikasi website, pembuatnya bisa si front end (javascript) bisa juga back end (dalam konteks ini Go). -Cookie merupakan salah satu aspek penting dalam pengembangan aplikasi web. Sangat sering kita membutuhkan sebuah data bisa disimpan dan diakses untuk keperluan aplikasi web kita, seperti pengecekan preferensi pengunjung, pengecekan status login tidak nya user. +Cookie merupakan salah satu aspek penting dalam pengembangan aplikasi web. Sangat sering kita memerlukan data di web yang perlu untuk disimpan dan mudah diakses oleh aplikasi web yang dikembangkan, diantaranya untuk keperluan seperti pengecekan preferensi pengunjung, pengecekan status login tidaknya user, dan lainnya. -Pada chapter ini kita akan belajar bagaimana cara membuat dan mengakses cookie di Go. +Pada chapter ini kita akan belajar bagaimana cara membuat dan mengakses cookie lewat Go. ## B.21.1. Praktek @@ -35,10 +35,10 @@ func main() { Variabel `cookieName` berisikan string, nantinya digunakan sebagai nama cookie. - - Rute `/` bertugas untuk membuat cookie baru (jika belum ada atau cookie sudah ada namun expired). + - Rute `/` bertugas untuk membuat cookie baru (jika belum ada atau cookie sudah ada namun expired) - Rute `/delete` mempunyai tugas untuk menghapus cookie, lalu redirect ke `/` sehingga cookie baru akan dibuat -OK, sekarang buat fungsi handler `ActionIndex()`. Di dalam fungsi ini, data berupa random string disimpan dalam cookie. +OK, sekarang buat fungsi handler `ActionIndex()`. Di dalam handler tersebut terdapat pembuatan cookie yang value-nya diisi data random string. ```go func ActionIndex(w http.ResponseWriter, r *http.Request) { @@ -62,24 +62,21 @@ func ActionIndex(w http.ResponseWriter, r *http.Request) { } ``` -Cookie bisa dikases lewat method `.Cookie()` milik objek `*http.Request`. Method ini mengembalikan 2 informasi: - - - Objek cookie - - Error, jika ada - -Pada kode di atas, ketika `storedCookie` nilainya bukanlah `nil` (berarti cookie dengan nama `cookieName` sudah dibuat), maka objek cookie tersebut disimpan dalam `c`. +Cookie bisa dikases lewat method `.Cookie()` milik objek `*http.Request`. Method ini mengembalikan 2 informasi yaitu objek cookie dan error, jika ada. Pembuatan cookie cukup mudah, tinggal cetak saja objek baru dari struct `http.Cookie`. -Jika `c.Value` adalah kosong, kita asumsikan bahwa cookie belum pernah dibuat (atau expired), maka kita buat cookie baru dengan data adalah random string. +Pada kode di atas, ketika `storedCookie` nilainya bukan `nil` (berarti cookie dengan nama `cookieName` sudah dibuat), maka objek `storedCookie` nilainya disimpan ke variabel `c`. + +Jika `c.Value` bernilai kosong, diasumsikan cookie belum pernah dibuat (atau expired), maka dibuatkanlah cookie baru dengan data adalah random string. > Untuk mempermudah generate random string, kita gunakan library bernama [Gubrak v2](https://github.com/novalagung/gubrak). Fungsi `gubrak.RandomString(32)` akan menghasilkan string 32 karakter. -Cookie bisa expired. Lama cookie aktif ditentukan lewat property `Expires`. Pada kode di atas expiration duration kita set selama 5 menit. +Cookie bisa expired. Lama aktifnya cookie ditentukan lewat property `Expires`. Di contoh durasi 5 menit digunakan sebagai expiration duration cookie. Gunakan `http.SetCookie()` untuk menyimpan cookie yang baru dibuat. -OK, selanjutnya buat handler `ActionDelete()`, seperti yang sudah disinggung di atas. Handler ini difungsikan untuk menghapus cookie dengan nama `cookieName`, lalu redirect ke `/` agar cookie baru diciptakan. +Selanjutnya buat handler `ActionDelete()`. Handler ini difungsikan untuk menghapus cookie dengan nama `cookieName`, lalu me-redirect request ke endpoint `/` agar cookie baru diciptakan. ```go func ActionDelete(w http.ResponseWriter, r *http.Request) { @@ -93,11 +90,11 @@ func ActionDelete(w http.ResponseWriter, r *http.Request) { } ``` -Cara menghapus cookie adalah dengan menge-set ulang cookie dengan nama yang sama, dengan isi property `Expires = time.Unix(0, 0)` dan `MaxAge = -1`. Tujuannya agar cookie expired. +Cara menghapus cookie adalah dengan menge-set ulang cookie dengan nama yang sama namun dengan isi property `Expires = time.Unix(0, 0)` dan `MaxAge = -1`. ## B.21.2. Testing -Jalankan aplikasi, lalu akses `/`. Sebuah random string akan muncul di layar, dan jika kita cek pada bagian response header, informasi cookie nya juga tampil. +Jalankan aplikasi, lalu akses endpoint `/`. Di halaman website akan muncul sebuah random string, dan jika kita cek pada bagian response header, informasi cookie-nya juga tampil. ![Cookie](images/B_cookie_1_cookie.png) @@ -126,7 +123,7 @@ Objek cookie memiliki beberapa property, beberapa di antaranya: --- diff --git a/content/B-custom-mux-multiplexer.md b/content/B-custom-mux-multiplexer.md index 1482d015e..88c9f6b04 100644 --- a/content/B-custom-mux-multiplexer.md +++ b/content/B-custom-mux-multiplexer.md @@ -1,16 +1,16 @@ # B.20. Custom Multiplexer -Pada chapter ini, kita akan belajar membuat custom multiplexer sendiri, lalu memanfaatkannya untuk mempermudah manajemen middleware. +Pada chapter ini, kita akan belajar cara membuat custom multiplexer sendiri, lalu memanfaatkannya untuk keperluan manajemen middleware. -Silakan salin project sebelumnya, chapter [B.19. Middleware http.Handler](/B-middleware-using-http-handler.html), ke folder baru untuk keperluan pembelajaran. +Silakan salin project sebelumnya (chapter [B.19. Middleware http.Handler](/B-middleware-using-http-handler.html)) ke folder baru untuk keperluan pembelajaran. ## B.20.1. Pembuatan Custom Mux -Pada chapter sebelumnya, default mux milik Go digunakan untuk routing dan implementasi middleware. Kali ini default mux tersebut tidak digunakan, kita akan buat mux baru. +Pada chapter sebelumnya, default mux milik Go digunakan untuk routing dan implementasi middleware. Kali ini default mux tersebut tidak digunakan karena mux baru akan dibuat. -Namun pembuatan mux baru tidaklah cukup, karena *naturally* mux baru tersebut tidak akan ada beda dengan default mux. Oleh karena itu agar lebih berguna, kita akan buat tipe mux baru, meng-embed `http.ServeMux` ke dalamnya, lalu membuat beberapa hal dalam struct tersebut. +Sebenarnya, pembuatan mux baru tidaklah cukup, karena mux baru tidak memiliki perbedaan signifikan dibanding default mux. Agar mux baru menjadi lebih berguna, mux baru tersebut perlu meng-embed `http.ServeMux` dan kita juga perlu mempersiapkan beberapa method. -OK, langsung saja kita praktekan. Ubah isi fungsi main menjadi seperti berikut. +OK, mari kita praktekan. Ubah isi fungsi `main()` menjadi seperti berikut. ```go mux := new(CustomMux) @@ -28,11 +28,11 @@ fmt.Println("server started at localhost:9000") server.ListenAndServe() ``` -Objek `mux` dicetak dari struct `CustomMux` yang jelasnya akan di buat. Struct ini di dalamnya meng-embed `http.ServeMux`. +Objek `mux` dicetak dari struct `CustomMux` yang mana nantinya struct ini dibuat dengan meng-embed `http.ServeMux`. -Registrasi middleware juga diubah, sekarang menggunakan method `.RegisterMiddleware()` milik mux. +Registrasi middleware juga perlu diubah, sekarang menggunakan method `.RegisterMiddleware()` milik `CustomMux`. -Pada file `middleware.go`, siapkan struct `CustomMux`. Selain meng-embed objek mux milik Go, siapkan juga satu variabel bertipe slice-dari-tipe-fungsi-middleware. +Selanjutnya, di file `middleware.go` siapkan struct `CustomMux`. Selain meng-embed objek mux milik Go, siapkan juga satu variabel bertipe `[]func(next http.Handler) http.Handler`. ```go type CustomMux struct { @@ -49,7 +49,7 @@ func (c *CustomMux) RegisterMiddleware(next func(next http.Handler) http.Handler } ``` -Lalu buat method `ServeHTTP`. Method ini diperlukan dalam custom mux agar memenuhi kriteria interface `http.Handler`. +Lalu buat method `ServeHTTP()`. Method ini diperlukan agar custom mux memenuhi kriteria interface `http.Handler`. ```go func (c *CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -63,9 +63,11 @@ func (c *CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { } ``` -Method `ServeHTTP()` milik mux adalah method yang pasti dipanggil pada web server, di setiap request yang masuk. +Method `ServeHTTP()` milik mux dipanggil setiap kali ada HTTP request. Dengan perubahan di atas, maka setiap kali ada request masuk pasti akan melewati middleware-middleware terlebih dahulu secara berurutan. -Dengan perubahan di atas, setiap kali ada request masuk pasti akan melewati middleware-middleware terlebih dahulu secara berurutan. Jika lolos middleware ke-1, lanjut ke-2; jika lolos middleware ke-2, lanjut ke-3; dan seterusnya. +- Jika lolos middleware ke-1, lanjut ke-2 +- Jika lolos middleware ke-2, lanjut ke-3 +- ... dan seterusnya ## B.20.2. Testing @@ -73,16 +75,18 @@ Jalankan aplikasi. ![Run the server](images/B_http_basic_auth_2_run_server.png) -Lalu test menggunakan `curl`, hasilnya adalah sama dengan pada chapter sebelumnya. +Lalu test menggunakan `curl`, hasilnya pasti sama dengan pada chapter sebelumnya. ![Consume API](images/B_http_basic_auth_3_test_api.png) -Jika ada keperluan untuk menambahkan middleware baru lainnya, cukup registrasikan lewat `.RegisterMiddleware()`. Source code menjadi lebih rapi dan nyaman untuk dilihat. +Jika ada keperluan untuk menambahkan middleware baru lainnya, cukup registrasikan lewat `.RegisterMiddleware()`. Pengaplikasian teknik custom mux ini membuat manajemen middleware menjadi lebih mudah. + +> Fun fact: semua *3rd party* router di Go (seperti Gin, Chi, Gorilla Mux, dan lainnya) menerapkan teknik custom multiplexer --- diff --git a/content/B-download-file.md b/content/B-download-file.md index 5cca97083..ed350ad12 100644 --- a/content/B-download-file.md +++ b/content/B-download-file.md @@ -1,10 +1,10 @@ # B.17. Download File -Setelah sebelumnya belajar cara untuk handle upload file, kali ini kita akan belajar bagaimana cara membuat handler yang hasilnya adalah download file. +Sebelumnya kita telah belajar cara untuk handle upload file, kali ini kita akan belajar bagaimana cara membuat HTTP handler yang menghasilkan response berbentuk download file. -Sebenarnya download file bisa dengan mudah di-implementasikan menggunakan teknik routing static file, langsung akses url dari public assets di browser. Namun outcome dari teknik ini sangat tergantung pada browser. Tiap browser memiliki behaviour berbeda, ada yang file tidak di-download melainkan dibuka di tab, ada yang ter-download. +Sebenarnya download file bisa dengan mudah di-implementasikan menggunakan teknik routing static file, lalu langsung mengakses url assets di browser. Namun outcome dari teknik ini sangat tergantung default konfigurasi browser. Tiap browser memiliki *behaviour* berbeda, ada yang akan merespon dengan membuka file di tab, ada juga yang merespon dengan men-download file tersebut. -Dengan menggunakan teknik berikut, file pasti akan ter-download. +Dengan penerapan teknik yang dibahas pada chapter ini, file bisa dipastikan di-download oleh browser sewaktu diakses. ## B.17.1. Struktur Folder Proyek @@ -12,7 +12,7 @@ OK, pertama siapkan terlebih dahulu proyek dengan struktur seperti gambar beriku ![Project structure](images/B_download_file_1_structure.png) -File yang berada di folder `files` adalah dummy, jadi anda bisa gunakan file apapun dengan jumlah berapapun untuk keperluan belajar. +File yang berada di folder `files` adalah dummy file. Silakan gunakan file apapun dengan jumlah berapapun untuk keperluan praktek ini. ## B.17.2. Front End @@ -35,9 +35,9 @@ Pertama siapkan dahulu template nya, isi file `view.html` dengan kode berikut. ``` -Tag `
    ` nantinya akan berisikan list semua file yang ada dalam folder `files`. Data list file didapat dari back end. Diperlukan sebuah AJAX untuk pengambilan data tersebut. +Tag `
      ` nantinya diisi dengan data list file yang ada dalam folder `files`. Data list file sendiri nantinya didapat dari API call ke back end. Di sini sebuah AJAX diperlukan untuk pengambilan data. -Siapkan sebuah fungsi dengan nama `Yo` atau bisa lainnya, fungsi ini berisikan closure `renderData()`, `getAllListFiles()`, dan method `init()`. Buat instance object baru dari `Yo`, lalu akses method `init()`, tempatkan dalam event `window.onload`. +Selanjutnya, siapkan sebuah fungsi dengan nama `Yo` atau bisa lainnya, fungsi ini berisikan closure `renderData()`, `getAllListFiles()`, dan method `init()`. Buat instance object baru dari `Yo`, lalu akses method `init()`, tempatkan dalam event `window.onload`. ```js function Yo() { @@ -81,7 +81,7 @@ var renderData = function (res) { }; ``` -Sedangkan closure `getAllListFiles()`, memiliki tugas untuk request ke back end, mengambil data list semua file. Request dilakukan dalam bentuk AJAX, nilai baliknya adalah data JSON. Setelah data sudah di tangan, fungsi `renderData()` dipanggil. +Sedangkan closure `getAllListFiles()`, memiliki tugas untuk request ke back end, mengambil data list semua file. Request dilakukan dalam bentuk AJAX, nilai baliknya adalah data JSON. Setelah data sudah didapatkan, fungsi `renderData()` dipanggil. ```js var getAllListFiles = function () { @@ -135,7 +135,6 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } ``` - Lalu siapkan juga route handler `/list-files`. Isi dari handler ini adalah membaca semua file yang ada pada folder `files` untuk kemudian dikembalikan sebagai output berupa JSON. Endpoint ini akan diakses oleh AJAX dari front end. ```go @@ -176,9 +175,9 @@ Fungsi `os.Getwd()` mengembalikan informasi absolute path di mana aplikasi di-ek > Fungsi `filepath.Join` akan menggabungkan item-item dengan path separator sesuai dengan sistem operasi di mana program dijalankan. `\` untuk Windows dan `/` untuk Linux/Unix. -Fungsi `filepath.Walk` berguna untuk membaca isi dari sebuah direktori, apa yang ada di dalamnya (file maupun folder) akan di-loop. Dengan memanfaatkan callback parameter kedua fungsi ini (yang bertipe `filepath.WalkFunc`), kita bisa mengamil informasi tiap item satu-per satu. +Fungsi `filepath.Walk` berguna untuk operasi list isi folder, apa yang ada di dalamnya (baik itu file maupun folder) akan diiterasi. Dengan memanfaatkan callback parameter kedua fungsi ini (yang bertipe `filepath.WalkFunc`), kita bisa mengambil informasi tiap item satu-per satu. -Selanjutnya siapkan handler untuk `/download`. Implementasi teknik download pada dasarnya sama pada semua bahasa pemrograman, yaitu dengan memainkan header **Content-Disposition** pada response. +Selanjutnya siapkan handler untuk `/download`. Implementasi teknik download pada dasarnya sama pada semua bahasa pemrograman, yaitu dengan memainkan isi dari header **Content-Disposition** pada HTTP response. ```go func handleDownload(w http.ResponseWriter, r *http.Request) { @@ -207,7 +206,9 @@ func handleDownload(w http.ResponseWriter, r *http.Request) { } ``` -**Content-Disposition** adalah salah satu ekstensi MIME protocol, berguna untuk menginformasikan browser bagaimana dia harus berinteraksi dengan output. Ada banyak jenis value content-disposition, salah satunya adalah `attachment`. Pada kode di atas, header `Content-Disposition: attachment; filename=filename.json` menghasilkan output response berupa attachment atau file, yang kemudian akan di-download oleh browser. +**Content-Disposition** adalah salah satu ekstensi MIME protocol, berguna untuk menginformasikan browser bagaimana mereka harus berinteraksi dengan output API endpoint. + +Ada banyak jenis value content-disposition, salah satunya adalah `attachment`. Pada kode di atas, header `Content-Disposition: attachment; filename=filename.json` menghasilkan output response berupa attachment atau file, yang kemudian akan di-download oleh browser. Objek file yang direpresentasikan variabel `f`, isinya di-copy ke objek response lewat statement `io.Copy(w, f)`. @@ -220,7 +221,7 @@ Jalankan program, akses rute `/`. List semua file dalam folder `files` muncul di --- diff --git a/content/B-form-upload-file.md b/content/B-form-upload-file.md index 52b93350e..d2c15e6ae 100644 --- a/content/B-form-upload-file.md +++ b/content/B-form-upload-file.md @@ -1,6 +1,6 @@ # B.13. Form Upload File -Pada bagian ini kita akan belajar bagaimana cara meng-handle upload file lewat form. Di beberapa bagian caranya mirip seperti pada chapter sebelumnya, hanya perlu ditambahkan proses untuk handling file yang di-upload. File tersebut disimpan ke dalam path/folder tertentu. +Pada bagian ini kita akan belajar bagaimana cara meng-handle upload file yang dilakukan via form submit di sisi front-end. Pada beberapa bagian, caranya mirip seperti pada chapter sebelumnya, perbedaannya kali ini handler berisi proses untuk handling file yang di-upload. Proses yang dimaksud adalah memproses payload file dari front-end untuk kemudian disimpan ke path/folder. ## B.13.1. Struktur Folder Proyek @@ -8,7 +8,7 @@ Sebelum mulai masuk ke bagian koding, siapkan terlebih dahulu file dan folder de ![Folder Structure](images/B_form_upload_file_1_structure.png) -Program sederhana yang akan kita buat, memiliki satu form dengan 2 inputan, alias dan file. Data file nantinya disimpan pada folder `files` yang telah dibuat, dengan nama sesuai nama file aslinya. Kecuali ketika user mengisi inputan alias, maka nama tersebut yang akan digunakan sebagai nama file tersimpan. +Program sederhana yang akan dibuat memiliki satu form dengan 2 inputan yaitu alias dan file. Di back-end, nantinya data file disimpan ke folder `files`, dengan default nama file sesuai nama file aslinya. Kecuali ketika user mengisi inputan alias, maka nama tersebut yang akan digunakan sebagai nama file tersimpan. ## B.13.2. Front End @@ -34,13 +34,13 @@ Di bagian front end, isi file `view.html` dengan kode berikut. Template file ini ``` -Perlu diperhatikan, pada tag `` perlu ditambahkan atribut `enctype="multipart/form-data"`, agar http request mendukung upload file. +Perlu diperhatikan, pada tag `` perlu ditambahkan atribut `enctype="multipart/form-data"`, agar HTTP request mendukung operasi upload file. -## B.13.3. Back End +## B.13.3. Back-End -Di layer back end ada cukup banyak package yang perlu di-import, seperti `os, io, path/filepath`, dan lainnya. Packages tersebut kita perlukan untuk handling file upload. +Di layer back-end ada cukup banyak package yang perlu di-import, seperti `os, io, path/filepath`, dan lainnya. Packages tersebut kita perlukan untuk handling file upload. -Pada fungsi `main()` siapkan 2 buah route handler, satu untuk landing page, dan satunya lagi digunakan ketika proses upload selesai (sama seperti pada chapter sebelumnya). +Pada fungsi `main()` siapkan 2 buah route handler, satu untuk landing page dan satunya lagi digunakan ketika proses upload selesai (sama seperti pada chapter sebelumnya). ```go package main @@ -97,7 +97,7 @@ func routeSubmitPost(w http.ResponseWriter, r *http.Request) { } ``` -Method `ParseMultipartForm()` digunakan untuk mem-parsing form data yang ada data file nya. Argumen `1024` pada method tersebut adalah `maxMemory`. Pemanggilan method tersebut membuat file yang terupload disimpan sementara pada memory dengan alokasi adalah sesuai dengan `maxMemory`. Jika ternyata kapasitas yang sudah dialokasikan tersebut tidak cukup, maka file akan disimpan dalam temporary file. +Method `ParseMultipartForm()` digunakan untuk mem-parsing form data yang ada data file-nya. Argumen `1024` pada method tersebut adalah `maxMemory`. Pemanggilan method tersebut membuat file yang terupload disimpan sementara pada memory dengan alokasi sesuai `maxMemory`. Jika ternyata kapasitas yang sudah dialokasikan tersebut tidak cukup, maka file akan disimpan dalam temporary file. Masih dalam fungsi `routeSubmitPost()`, tambahkan kode untuk mengambil data alias dan file. @@ -118,13 +118,13 @@ if err != nil { } ``` -Statement `r.FormFile("file")` digunakan untuk mengambil file yg di upload, mengembalikan 3 objek: +Statement `r.FormFile("file")` digunakan untuk mengambil file yg di upload, dan mengembalikan 3 objek: - Objek bertipe multipart.File (yang merupakan turunan dari `*os.File`) - Informasi header file (bertipe `*multipart.FileHeader`) - Dan `error` jika ada -Tahap selanjutnya adalah, menambahkan kode membuat file baru, yang nantinya file ini akan diisi dengan isi dari file yang ter-upload. Jika inputan `alias` di-isi, maka nama nilai inputan tersebut dijadikan sebagai nama file. +Tahap selanjutnya adalah menambahkan kode membuat file baru, yang nantinya file ini akan diisi dengan isi dari file yang ter-upload. Jika inputan `alias` di-isi, maka nama nilai inputan tersebut dijadikan sebagai nama file. ```go filename := handler.Filename @@ -148,18 +148,18 @@ if _, err := io.Copy(targetFile, uploadedFile); err != nil { w.Write([]byte("done")) ``` -Fungsi `filepath.Ext` digunakan untuk mengambil ekstensi dari sebuah file. Pada kode di atas, `handler.Filename` yang berisi nama file terupload diambil ekstensinya, lalu digabung dengan `alias` yang sudah terisi. +Fungsi `filepath.Ext()` digunakan untuk mengambil ekstensi dari sebuah file. Pada kode di atas, `handler.Filename` yang berisi nama file terupload diambil ekstensinya, lalu digabung dengan `alias` yang sudah terisi. -Fungsi `filepath.Join` berguna untuk pembentukan path. +Fungsi `filepath.Join()` berguna untuk pembentukan path. -Fungsi `os.OpenFile` digunakan untuk membuka file. Fungsi ini membutuhkan 3 buah parameter: +Fungsi `os.OpenFile()` digunakan untuk membuka file. Fungsi ini membutuhkan 3 buah argument parameter dalam pemanggilannya: - - Parameter pertama merupakan path atau lokasi dari file yang ingin di buka + - Parameter pertama merupakan path atau lokasi dari file yang ingin di buka. - Parameter kedua adalah flag mode, apakah *read only*, *write only*, atau keduanya, atau lainnya. - `os.O_WRONLY|os.O_CREATE` maknanya, file yang dibuka hanya akan bisa di tulis saja (*write only* konsantanya adalah `os.O_WRONLY`), dan file tersebut akan dibuat jika belum ada (konstantanya `os.O_CREATE`). - Sedangkan parameter terakhir adalah permission dari file, yang digunakan dalam pembuatan file itu sendiri. -Fungsi `io.Copy` akan mengisi konten file parameter pertama (`targetFile`) dengan isi parameter kedua (`uploadedFile`). File kosong yang telah kita buat tadi akan diisi dengan data file yang tersimpan di memory. +Fungsi `io.Copy()` mengisi konten file parameter pertama (`targetFile`) dengan isi parameter kedua (`uploadedFile`). File kosong yang telah kita buat tadi akan diisi dengan data file yang tersimpan di memory. > Nantinya pada salah satu pembahasan pada chapter [B.16. AJAX Multiple File Upload](/B-ajax-multi-upload.html) akan dijelaskan cara handling file upload dengan metode yang lebih efektif dan hemat memori, yaitu menggunakan `MultipartReader`. @@ -172,7 +172,7 @@ Jalankan program, test hasilnya lewat browser. --- diff --git a/content/B-form-value.md b/content/B-form-value.md index 3a86c20ec..a356a90c3 100644 --- a/content/B-form-value.md +++ b/content/B-form-value.md @@ -1,10 +1,10 @@ # B.12. Form Value -Pada chapter ini kita akan belajar bagaimana cara untuk submit data, dari form di layer front end, ke back end. +Pada chapter ini kita akan belajar bagaimana cara untuk submit data dari form di front-end untuk dikirim ke back-end melalui API call (HTTP request). -# B.12.1. Front End +# B.12.1. Front-End -Pertama siapkan folder project baru, dan sebuah file template view `view.html`. Pada file ini perlu didefinisikan 2 buah template, yaitu `form` dan `result`. Template pertama (`form`) dijadikan landing page program, isinya beberapa inputan untuk submit data. +Pertama siapkan folder project baru dan sebuah file template view `view.html`. Pada file ini perlu didefinisikan 2 buah template, yaitu `form` dan `result`. Template pertama (`form`) dijadikan landing page program, isinya beberapa inputan untuk submit data. ```html {{define "form"}} @@ -30,7 +30,7 @@ Pertama siapkan folder project baru, dan sebuah file template view `view.html`. {{end}} ``` -Aksi dari form di atas adalah `/process`, yang di mana url tersebut nantinya akan mengembalikan output berupa html hasil render template `result`. Silakan tulis template result berikut dalam `view.html` (jadi file view ini berisi 2 buah template). +Aksi dari form di atas adalah `/process`, yang mana url tersebut nantinya akan mengembalikan output berupa html hasil render template `result`. Silakan tulis template result berikut dalam `view.html` (jadi file view ini berisi 2 buah template). ```html {{define "result"}} @@ -47,7 +47,7 @@ Aksi dari form di atas adalah `/process`, yang di mana url tersebut nantinya aka {{end}} ``` -## B.12.2. Back End +## B.12.2. Back-End Buat file `main.go`. Dalam file ini 2 buah route handler diregistrasikan. @@ -70,7 +70,7 @@ func main() { } ``` -Handler route `/` dibungkus dalam fungsi bernama `routeIndexGet`. Di dalamnya, template `form` dalam file template `view.html` akan di-render ke view. Request dalam handler ini hanya dibatasi untuk method GET saja, request dengan method lain akan menghasilkan response 400 Bad Request. +Handler route `/` dibungkus dalam fungsi bernama `routeIndexGet()`. Di dalamnya, template `form` dalam file template `view.html` akan di-render ke view. Request dalam handler ini hanya dibatasi untuk method GET saja, request dengan method lain akan menghasilkan response `400 Bad Request`. ```go func routeIndexGet(w http.ResponseWriter, r *http.Request) { @@ -88,7 +88,7 @@ func routeIndexGet(w http.ResponseWriter, r *http.Request) { } ``` -Fungsi `routeSubmitPost` yang merupakan handler route `/process`, berisikan proses yang mirip seperti handler route `/`, yaitu parsing `view.html` untuk di ambil template `result`-nya. Selain itu, pada handler ini ada proses pengambilan data yang dikirim dari form ketika di-submit, untuk kemudian disisipkan ke template view. +Fungsi `routeSubmitPost()` yang merupakan handler route `/process`, berisikan proses yang mirip seperti handler route `/`, yaitu parsing `view.html` untuk di ambil template `result`-nya. Selain itu, pada handler ini ada proses pengambilan data yang dikirim dari form submit, untuk kemudian disisipkan ke template view. ```go func routeSubmitPost(w http.ResponseWriter, r *http.Request) { @@ -115,15 +115,15 @@ func routeSubmitPost(w http.ResponseWriter, r *http.Request) { } ``` -Ketika user submit ke `/process`, maka data-data yang ada di form input dikirim. Method `ParseForm()` pada statement `r.ParseForm()` berguna untuk parsing form data yang dikirim dari view, sebelum akhirnya bisa diambil data-datanya. Method tersebut mengembalikan data `error` jika proses parsing gagal (kemungkinan karena data yang dikirim ada yang tidak valid). +Ketika user submit ke `/process`, maka data-data yang ada di form input dikirim. Method `ParseForm()` pada statement `r.ParseForm()` berguna untuk parsing form data yang dikirim dari view, sebelum akhirnya bisa diambil datanya. Method tersebut mengembalikan `error` jika proses parsing gagal (kemungkinan karena data yang dikirim ada yang tidak valid). -Pengambilan data yang dikirim dilakukan lewat method `FormValue()`. Contohnya seperti pada kode di atas, `r.FormValue("name")`, akan mengembalikan data inputan `name` (data dari inputan ``). +Pengambilan data dilakukan lewat method `FormValue()`. Contohnya seperti pada kode di atas, `r.FormValue("name")`, akan mengembalikan data inputan `name` (data dari inputan ``). Selain lewat method `FormValue()`, pengaksesan data juga bisa dilakukan dengan cara mengakses property `Form` terlebih dahulu, kemudian mengakses method `Get()`. Contohnya seperti `r.Form.Get("message")`, yang akan menghasilkan data inputan `message`. Hasil dari kedua cara di atas adalah sama. Setelah data dari form sudah ditangkap oleh back-end, data ditampung dalam variabel `data` yang bertipe `map[string]string`. Variabel `data` tersebut kemudian disisipkan ke view, lewat statement `tmpl.Execute(w, data)`. -## B.12.3. Test +## B.12.3. Testing OK, sekarang coba jalankan program yang telah kita buat, dan cek hasilnya. @@ -132,7 +132,7 @@ OK, sekarang coba jalankan program yang telah kita buat, dan cek hasilnya. --- diff --git a/content/B-golang-web-hello-world.md b/content/B-golang-web-hello-world.md index 6acbd481a..21dcba333 100644 --- a/content/B-golang-web-hello-world.md +++ b/content/B-golang-web-hello-world.md @@ -1,10 +1,12 @@ # B.1. Golang Web App: Hello World -Pada serial chapter B ini, kita masih tetap akan belajar tentang topik fundamental atau dasar tapi lebih spesifik ke arah web development. Kita awali dengan pembahasan bagaimana cara membuat aplikasi web "Hello World" sederhana menggunakan Go. +Pada serial chapter B ini, fokus pembelajaran masih tetap tentang topik-topik fundamental atau dasar, tapi lebih spesifik ke area yang berhubungan dengan web development atau pengembangan web. + +Pembahasan diawali dengan pembuatan aplikasi web "Hello World" sederhana menggunakan Go. ## B.1.1. Pembuatan Aplikasi -Mari belajar dengan praktek langsung. Pertama buat folder project baru dengan isi `main.go`, tentukan package-nya sebagai `main`, lalu import package `fmt` dan `net/http`. +Mari belajar sambil praktik. Pertama buat folder project baru dengan isi `main.go`, tentukan package-nya sebagai `main`, lalu import package `fmt` dan `net/http`. ```go package main @@ -13,12 +15,12 @@ import "fmt" import "net/http" ``` -Setelah itu, siapkan dua buah fungsi, masing-masing fungsi memiliki skema parameter yang sama seperti berikut. +Setelah itu, siapkan dua buah fungsi, masing-masing memiliki skema parameter yang sama: - Parameter ke-1 bertipe `http.ResponseWrite` - Parameter ke-2 bertipe `*http.Request` -Fungsi dengan struktur di atas diperlukan oleh `http.HandleFunc` untuk keperluan penanganan request ke rute yang ditentukan. Berikut merupakan dua fungsi yang dimaksud. +Fungsi dengan struktur di atas diperlukan oleh `http.HandleFunc` sebagai handler untuk keperluan penanganan request ke rute yang ditentukan. Berikut adalah dua fungsi yang dimaksud: ```go func handlerIndex(w http.ResponseWriter, r *http.Request) { @@ -32,11 +34,11 @@ func handlerHello(w http.ResponseWriter, r *http.Request) { } ``` -Method `Write()` milik parameter pertama (yang bertipe `http.ResponseWrite`), digunakan untuk meng-output-kan nilai balik data. Argumen method adalah data yang ingin dijadikan output, ditulis dalam bentuk `[]byte`. +Method `Write()` milik parameter pertama (yang bertipe `http.ResponseWrite`), digunakan untuk meng-output-kan data ke HTTP response. Argumen method adalah data yang ingin dijadikan output, dituliskan dalam bentuk `[]byte`. -Pada contoh ini, data yang akan kita tampilkan bertipe string, maka perlu dilakukan casting dari `string` ke `[]byte`. Contohnya bisa dilihat seperta pada kode di atas, di bagian `w.Write([]byte(message))`. +Pada contoh ini, data yang akan kita tampilkan bertipe string, maka perlu dilakukan casting dari `string` ke `[]byte`. Praktiknya bisa dilihat seperta pada kode di atas, di bagian `w.Write([]byte(message))`. -Selanjutnya, siapkan fungsi `main()` dengan isi di dalamnya adalah beberapa rute atau route, dengan aksi adalah kedua fungsi yang sudah disiapkan di atas. Tak lupa siapkan juga kode untuk start server. +Selanjutnya, siapkan fungsi `main()` dengan isi di dalamnya adalah beberapa rute (atau *route*), dengan aksi adalah kedua fungsi yang sudah disiapkan di atas. Tak lupa siapkan juga kode untuk start web server. ```go func main() { @@ -53,11 +55,11 @@ func main() { } ``` -Fungsi `http.HandleFunc()` digunakan untuk routing. Parameter pertama adalah rute dan parameter ke-2 adalah handler-nya. +Fungsi `http.HandleFunc()` digunakan untuk keperluan routing. Parameter pertama adalah rute dan parameter ke-2 adalah handler-nya. Fungsi `http.ListenAndServe()` digunakan membuat sekaligus start server baru, dengan parameter pertama adalah alamat web server yang diiginkan (bisa diisi host, host & port, atau port saja). Parameter kedua merupakan object mux atau multiplexer. -> Dalam chapter ini kita menggunakan default mux yang sudah disediakan oleh Go, jadi untuk parameter ke-2 cukup isi dengan `nil`. +> Dalam chapter ini kita menggunakan *default* mux yang sudah disediakan oleh Go, jadi untuk parameter ke-2 cukup isi dengan `nil`. Ok, sekarang program sudah siap, jalankan menggunakan `go run`. @@ -71,7 +73,7 @@ Berikut merupakan penjelasan detail per-bagian program yang telah kita buat dari #### ◉ Penggunaan `http.HandleFunc()` -Fungsi ini digunakan untuk **routing**, menentukan aksi dari sebuah url tertentu ketika diakses (di sini url tersebut kita sebut sebagai rute/route). Rute dituliskan dalam `string` sebagai parameter pertama, dan aksi-nya sendiri dibungkus dalam fungsi (bisa berupa closure) yang ditempatkan pada parameter kedua (kita sebut sebagai handler). +Fungsi ini digunakan untuk **routing**, menentukan aksi dari pengaksesan URL/rute tertentu. Rute dituliskan dalam tipe data `string` sebagai parameter pertama, dan aksi-nya sendiri dibungkus dalam fungsi (bisa berupa closure) pada parameter kedua (biasanya disebut sebagai handler). Pada kode di atas, tiga buah rute didaftarkan: @@ -79,9 +81,9 @@ Pada kode di atas, tiga buah rute didaftarkan: - Rute `/index` dengan aksi adalah sama dengan `/`, yaitu fungsi `handlerIndex()` - Rute `/hello` dengan aksi fungsi `handlerHello()` -Ketika rute-rute tersebut diakses lewat browser, outpunya adalah isi-handler dari rute yang bersangkutan. Kebetulan pada chapter ini, ketiga rute tersebut outputnya adalah sama, yaitu berupa string. +Ketika rute-rute di atas diakses lewat browser, outpunya adalah isi-handler rute yang bersangkutan. Kebetulan pada chapter ini, ketiga rute tersebut outputnya adalah sama, yaitu berupa string. -> Pada contoh di atas, ketika rute yang tidak terdaftar diakses, secara otomatis handler rute `/` akan terpanggil. +> Pada contoh di atas, ketika rute yang tidak terdaftar diakses, maka secara otomatis handler rute `/` yang dipanggil. #### ◉ Penjelasan Mengenai **Handler** @@ -99,7 +101,7 @@ func handlerIndex(w http.ResponseWriter, r *http.Request) { } ``` -Output dari rute, dituliskan di dalam handler menggunakan method `Write()` milik objek `ResponseWriter` (parameter pertama). Output bisa berupa apapun, untuk output text tinggal lakukan casting dari tipe `string` ke `[]byte`, aturan ini juga berlaku untuk beberapa jenis output lainnya seperti html dan json, namun response header `Content-Type` perlu disesuaikan. +Output dari rute dituliskan di dalam handler menggunakan method `Write()` milik objek `ResponseWriter` (parameter pertama). Output bisa berupa apapun, untuk output text tinggal lakukan casting dari tipe `string` ke `[]byte`, aturan ini juga berlaku untuk banyak jenis output lainnya seperti HTML, XML, JSON, dan lainnya (dengan catatan response header `Content-Type`-nya juga perlu disesuaikan). Pada contoh program yang telah kita buat, handler `Index()` memunculkan text `"Welcome"`, dan handler `Hello()` memunculkan text `"Hello world!"`. @@ -107,7 +109,7 @@ Sebuah handler bisa dipergunakan pada banyak rute, bisa dilihat pada di atas han #### ◉ Penggunaan `http.ListenAndServe()` -Fungsi ini digunakan untuk membuat web server baru. Pada contoh yang telah dibuat, web server di-*start* pada port `9000` (bisa dituliskan dalam bentuk `localhost:9000` atau cukup `:9000` saja). +Fungsi ini digunakan untuk membuat web server baru. Pada contoh yang telah dibuat, web server di-*start* pada port `9000` (bisa dituliskan dalam bentuk `localhost:9000`, `0.0.0.0:9000`, atau cukup `:9000` saja). ```go var address = ":9000" @@ -139,21 +141,19 @@ if err != nil { Informasi host/port perlu dimasukan dalam property `.Addr` milik objek server. Lalu dari objek tersebut panggil method `.ListenAndServe()` untuk start web server. -Kelebihan menggunakan `http.Server` salah satunya adalah kemampuan untuk mengubah beberapa konfigurasi default web server Go. - -Contoh, pada kode berikut, timeout untuk read request dan write request di ubah menjadi 10 detik. +Kelebihan menggunakan `http.Server` salah satunya adalah kemampuan untuk mengubah beberapa konfigurasi default web server Go. Contohnya bisa dilihat pada kode berikut, timeout untuk read request dan write request di ubah menjadi 10 detik. -``` +```go server.ReadTimeout = time.Second * 10 server.WriteTimeout = time.Second * 10 ``` -Ada banyak lagi property dari struct `http.Server` ini, yang pastinya akan dibahas pada pembahasan-pembahasan selanjutnya. +Struct `http.Server` memiliki cukup banyak property lainnya, yang pastinya akan dibahas pada pembahasan-pembahasan selanjutnya. --- diff --git a/content/B-http-basic-auth.md b/content/B-http-basic-auth.md index 5350c63e3..5fed853e2 100644 --- a/content/B-http-basic-auth.md +++ b/content/B-http-basic-auth.md @@ -1,17 +1,19 @@ # B.18. HTTP Basic Authentication -HTTP Basic Auth adalah salah satu teknik otentikasi http request. Metode ini membutuhkan informasi username dan password untuk disisipkan dalam header request (dengan format tertentu), jadi cukup sederhana, tidak memerlukan cookies maupun session. Lebih jelasnya silakan baca [RFC-7617](https://tools.ietf.org/html/rfc7617). +HTTP Basic Auth adalah salah satu spesifikasi yang mengatur otentikasi pada HTTP request. Metode ini mewajibkan client request untuk menyertakan username dan password dalam header request. Dengan menerapkan basic auth maka kita tidak perlu menggunakan token untuk mendapatkan session. -Informasi username dan password tidak serta merta disisipkan dalam header, informasi tersebut harus di-encode terlebih dahulu ke dalam format yg sudah ditentukan sesuai spesifikasi, sebelum dimasukan ke header. +> Lebih jelasnya mengenai spesifikasi Basic Auth bisa di lihat di[RFC-7617](https://tools.ietf.org/html/rfc7617) -Berikut adalah contoh penulisan basic auth. +Informasi username dan password harus di-encode terlebih dahulu ke format yg sudah ditentukan sesuai spesifikasi, kemudian dijadikan value dari header `Authentication`. + +Berikut adalah contoh format penulisan basic auth: ```js // Request header Authorization: Basic c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA== ``` -Informasi disisipkan dalam request header dengan key `Authorization`, dan value adalah `Basic` spasi hasil enkripsi dari data username dan password. Data username dan password digabung dengan separator tanda titik dua (`:`), lalu di-encode dalam format encoding Base 64. +Informasi disisipkan dalam request header dengan key `Authorization`, dan value adalah `Basic` diikut karakter spasi dan hasil encode terhadap data username dan password. Data username dan password digabung dengan separator tanda titik dua (`:`) lalu di-encode dalam format encoding Base64. ```js // Username password encryption @@ -19,14 +21,14 @@ base64encode("someusername:somepassword") // Hasilnya adalah c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA== ``` -Golang menyediakan fungsi untuk meng-handle request basic auth dengan cukup mudah, jadi tidak perlu untuk memparsing header request terlebih dahulu untuk mendapatkan informasi username dan password. +Go menyediakan fasilitas untuk mengambil informasi basic auth dari suatu HTTP request dengan mudah, tanpa perlu untuk memparsing header request terlebih dahulu secara manual. ## B.18.1. Struktur Folder Proyek dan Endpoint -Ok, mari kita praktekan. Pada chapter ini kita akan membuat sebuah web service sederhana, isinya satu buah endpoint. Endpoint ini kita manfaatkan sebagai dua endpoint, dengan pembeda adalah informasi pada query string-nya. +Pada chapter ini kita akan membuat sebuah web service sederhana, isinya hanya satu buah endpoint. Endpoint ini didesain untuk bisa menerima query parameter atau tanpa query parameter. - - Endpoint `/student`, menampilkan semua data siswa. - - Endpoint `/student?id=s001`, menampilkan data siswa sesuai dengan id yang di minta. + - Endpoint `/student` menghasilkan response berisi semua data siswa + - Endpoint `/student?id=s001` menghasilkan response berisi data siswa sesuai dengan id yang di minta Data siswa sendiri merupakan slice object yang disimpan di variabel global. @@ -56,12 +58,16 @@ func main() { } ``` -Siapkan handler untuk rute `/student`. +Lalu siapkan handler untuk rute `/student`. ```go func ActionStudent(w http.ResponseWriter, r *http.Request) { - if !Auth(w, r) { return } - if !AllowOnlyGET(w, r) { return } + if !Auth(w, r) { + return + } + if !AllowOnlyGET(w, r) { + return + } if id := r.URL.Query().Get("id"); id != "" { OutputJSON(w, SelectStudent(id)) @@ -74,15 +80,15 @@ func ActionStudent(w http.ResponseWriter, r *http.Request) { Di dalam rute `/student` terdapat beberapa validasi. - - Validasi `!Auth(w, r)`; Nantinya akan kita buat fungsi `Auth()` untuk mengecek apakah request merupakan valid basic auth request atau tidak. - - Validasi `!AllowOnlyGET(w, r)`; Nantinya juga akan kita siapkan fungsi `AllowOnlyGET()`, gunanya untuk memastikan hanya request dengan method `GET` yang diperbolehkan masuk. + - Validasi `!Auth(w, r)`; Nantinya kita siapkan fungsi `Auth()` yang gunanya adalah untuk mengecek apakah request merupakan valid basic auth request atau tidak. + - Validasi `!AllowOnlyGET(w, r)`; Akan dibuat juga fungsi `AllowOnlyGET()`, tugasnya memastikan hanya request dengan method `GET` yang diperbolehkan masuk. -Setelah request lolos dari 2 validasi di atas, kita cek lagi apakah request ini memiliki parameter student id. +Setelah request lolos dari 2 validasi di atas, lanjut ke pengecekan berikutnya yaitu mendeteksi apakah request memiliki parameter student id. - - Ketika tidak ada parameter student id, maka endpoint ini mengembalikan semua data user yang ada, lewat pemanggilan fungsi `GetStudents()`. - - Sedangkan jika ada parameter student id, maka hanya user dengan id yg diinginkan yg dijadikan nilai balik, lewat fungsi `SelectStudent(id)`. + - Ketika tidak ada parameter student id, maka endpoint ini mengembalikan semua data user yang ada. Fungsi `GetStudents()` dieksekusi. + - Sedangkan jika ada parameter student id, maka hanya user dengan id yg diinginkan yg dijadikan nilai balik. Fungsi `SelectStudent(id)` dieksekusi. -Selanjutnya tambahkan satu fungsi lagi di main, `OutputJSON()`. Fungsi ini digunakan untuk mengkonversi data menjadi JSON string. +Selanjutnya tambahkan satu fungsi lagi di `main()` yaitu `OutputJSON()`. Fungsi ini digunakan konversi data ke bentuk JSON string. ```go func OutputJSON(w http.ResponseWriter, o interface{}) { @@ -97,11 +103,11 @@ func OutputJSON(w http.ResponseWriter, o interface{}) { } ``` -Konversi dari objek atau slice ke JSON string bisa dilakukan dengan memanfaatkan `json.Marshal`. Untuk lebih jelasnya silakan baca lagi chapter [A.53. JSON Data](/A-json.html). +Konversi dari objek atau slice ke JSON string dilakukan via `json.Marshal()`. Lebih jelasnya mengenai fungsi tersebut di bahas di chapter [A.53. JSON Data](/A-json.html). ## B.18.3. Data `Student` -Buka file `student.go`, siapkan struct `Student` dan variabel untuk menampung data yang bertipe `[]Student`. Data inilah yang dijadikan nilai balik di endpoint yang sudah dibuat. +Buka file `student.go`, siapkan struct `Student` dan variabel untuk menampung data yang bertipe `[]Student`. Data inilah yang nantinya dijadikan nilai balik endpoint `/student`. ```go package main @@ -115,7 +121,7 @@ type Student struct { } ``` -Buat fungsi `GetStudents()`, fungsi ini mengembalikan semua data student. Dan buat juga fungsi `SelectStudent(id)`, fungsi ini mengembalikan data student sesuai dengan id terpilih. +Buat fungsi `GetStudents()`, fungsi ini mengembalikan semua data student. Buat juga fungsi `SelectStudent(id)`, fungsi ini mengembalikan data student sesuai dengan id terpilih. ```go func GetStudents() []*Student { @@ -133,7 +139,7 @@ func SelectStudent(id string) *Student { } ``` -*Last but not least*, implementasikan fungsi `init()`, buat beberapa dummy data untuk ditampung pada variabel `students`. +*Last but not least*, implementasikan fungsi `init()` yang didalamnya berisi pembuatan beberapa dummy data untuk ditampung variabel `students`. > Fungsi `init()` adalah fungsi yang secara otomatis dipanggil ketika package tersebut di import atau di run. @@ -147,7 +153,7 @@ func init() { ## B.18.4. Fungsi `Auth()` dan `AllowOnlyGET()` -Selanjutnya, kita perlu menyiapkan beberapa fungsi yg digunakan pada `main.go`, yaitu `Auth()` dan `AllowOnlyGET()`. +Selanjutnya, ada dua fungsi lainnya yang perlu dipersiapkan yaitu `Auth()` dan `AllowOnlyGET()`. #### ◉ Fungsi `Auth()` @@ -186,11 +192,11 @@ Fungsi `r.BasicAuth()` mengembalikan 3 informasi: 2. Password 3. Nilai balik ke-3 ini adalah representasi valid tidak nya basic auth request yang sedang berlangsung -Jika basic auth request tidak valid, maka tampilkan pesan error sebagai nilai balik. Sedangkan jika basic auth adalah valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang ada di aplikasi kita. +Error dimunculkan ketika basic auth terdeteksi adalah tidak valid. Sedangkan jika ternyata valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang sudah di-*hardcode*. #### ◉ Fungsi `AllowOnlyGET()` -Fungsi ini bertugas untuk memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka akan dianggap invalid request. +Fungsi ini bertugas memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka dianggap invalid request. ```go func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool { @@ -205,17 +211,17 @@ func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool { ## B.18.5. Testing -Semuanya sudah siap, jalankan aplikasi. +Semuanya sudah siap, sekarang jalankan aplikasi. ```bash go run *.go ``` -Jangan menggunakan `go run main.go`, dikarenakan dalam package `main` terdapat beberapa file lain yang harus di-ikut-sertakan pada saat runtime. +Jangan menggunakan `go run main.go`, dikarenakan dalam package `main` terdapat beberapa file lain yang harus diikutsertakan pada saat runtime. ![Run the server](images/B_http_basic_auth_2_run_server.png) -Test web service kecil ini menggunakan command `curl`. +Test web service yang telah dibuat menggunakan command `curl`. ```bash $ curl -X GET --user batman:secret http://localhost:9000/student @@ -227,7 +233,7 @@ $ curl -X GET --user batman:secret http://localhost:9000/student?id=s001 --- diff --git a/content/B-http-method-basic.md b/content/B-http-method-basic.md index 20b6b4ab5..3d930f39d 100644 --- a/content/B-http-method-basic.md +++ b/content/B-http-method-basic.md @@ -1,14 +1,14 @@ # B.11. HTTP Method: POST & GET -Setelah sebelumnya kita telah mempelajari banyak hal yang berhubungan dengan template view, kali ini topik yang terpilih adalah berbeda, yaitu mengenai penanganan http request di back end. +Sampai chapter ini, terhitung kita telah mempelajari banyak hal yang berhubungan dengan template view. Kali ini topik yang akan dibahas sedikit berbeda, yaitu mengenai penanganan HTTP request di back-end. -Sebuah route handler pada dasarnya bisa menerima segala jenis request, dalam artian: apapun jenis HTTP method-nya maka akan tetap masuk ke satu handler (seperti **POST**, **GET**, dan atau lainnya). Untuk memisah request berdasarkan method-nya, bisa menggunakan seleksi kondisi. +Sebuah route handler pada dasarnya bisa menerima segala jenis request, apapun jenis HTTP method-nya maka akan tetap masuk ke satu handler (seperti **POST**, **GET**, dan atau lainnya). Pengategorian request berdasarkan HTTP method bisa dilakukan menggunakan seleksi kondisi. -> Pada chapter lain kita akan belajar teknik routing yg lebih advance dengan bantuan routing library. +> Pada chapter lain kita akan belajar teknik routing yg lebih advance dengan bantuan *3rd party* routing library. ## B.11.1. Praktek -Silakan pelajari dan praktekkan kode berikut. +Mari coba praktekan. Disiapkan sebuah handler untuk rute `/` yang didalamnya ada pengecekan seleksi kondisi berdasarkan HTTP method. ```go package main @@ -33,28 +33,31 @@ func main() { } ``` -Struct `*http.Request` memiliki property bernama `Method` yang bisa digunakan untuk mengecek method daripada request yang sedang berjalan. +Struct `*http.Request` memiliki property bernama `Method`, isinya informasi HTTP method dari request. -Pada contoh di atas, request ke rute `/` dengan method POST akan menghasilkan output text `post`, sedangkan method GET menghasilkan output text `get`. +- Jika HTTP method adalah `POST`, maka text `post` dijadikan nilai response +- Jika HTTP method adalah `GET`, maka text `get` dijadikan nilai response -## B.11.2. Test +## B.11.2. Testing -Gunakan [Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en), atau tools sejenisnya untuk mempermudah testing. Berikut adalah contoh request dengan method GET. +Gunakan [Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en), atau tools sejenisnya untuk mempermudah testing. + +Berikut adalah contoh request dengan method GET. ![Request GET](images/B_http_method_basic_1_get.png) -Sedangkan di bawah ini adalah untuk method POST. +Dan di bawah ini adalah contoh request dengan method POST. ![Request POST](images/B_http_method_basic_2_post.png) -Jika method yang digunakan adalah selain POST dan GET, maka sesuai source code di atas, harusnya request akan menghasilkan response **400 Bad Request**. Di bawah ini adalah contoh request dengan method **PUT**. +Jika method yang digunakan adalah selain POST dan GET, maka web server menghasilkan response **400 Bad Request**. Di bawah ini adalah contoh request dengan method **PUT**. ![400 Bad Request](images/B_http_method_basic_3_bad_request.png) --- diff --git a/content/B-middleware-using-http-handler.md b/content/B-middleware-using-http-handler.md index 8790450d4..1a82a6945 100644 --- a/content/B-middleware-using-http-handler.md +++ b/content/B-middleware-using-http-handler.md @@ -1,35 +1,41 @@ # B.19. Middleware `http.Handler` -Pada chapter ini, kita akan belajar penggunaan interface `http.Handler` untuk implementasi custom middleware. Kita akan menggunakan sample proyek pada chapter sebelumnya [B.18. HTTP Basic Auth](/B-http-basic-auth.html) sebagai dasar bahan pembahasan chapter ini. +Pada chapter ini, topik yang dibahas adalah penerapan interface `http.Handler` untuk implementasi custom middleware. Kita gunakan sample proyek pada chapter sebelumnya [B.18. HTTP Basic Auth](/B-http-basic-auth.html) sebagai dasar bahan pembahasan chapter ini. -> Apa itu middleware? Istilah middleware berbeda-beda di tiap bahasa/framework. NodeJS dan Rails ada istilah middleware. Pada pemrograman Java Enterprise, istilah filters digunakan. Pada C# istilahnya adalah delegate handlers. Definisi dari middleware sendiri versi penulis, sebuah blok kode yang dipanggil sebelum ataupun sesudah http request di proses. +> Apa itu middleware? +> +> Istilah middleware berbeda-beda di tiap bahasa/framework. Di NodeJS dan Rails ada istilah middleware. Pada pemrograman Java Enterprise, istilah filters digunakan. Pada C# middleware disebut dengan delegate handlers. Definisi sederhana middleware adalah sebuah blok kode yang dipanggil sebelum ataupun sesudah http request di proses. -Pada chapter sebelumnya, kalau dilihat, ada beberapa proses yang dijalankan dalam handler rute `/student`, yaitu pengecekan otentikasi dan pengecekan method. Misalnya terdapat rute lagi, maka dua validasi tersebut juga harus dipanggil lagi dalam handlernya. +Pada chapter sebelumnya, terdapat beberapa proses yang dijalankan dalam handler rute `/student`, yaitu pengecekan otentikasi dan pengecekan HTTP method. Misalnya terdapat rute lagi, maka dua validasi tersebut juga harus dipanggil lagi dalam handlernya. ```go func ActionStudent(w http.ResponseWriter, r *http.Request) { - if !Auth(w, r) { return } - if !AllowOnlyGET(w, r) { return } + if !Auth(w, r) { + return + } + if !AllowOnlyGET(w, r) { + return + } // ... } ``` -Jika ada banyak rute, apa yang harus kita lakukan? salah satu solusi yang bisa digunakan adalah dengan memanggil fungsi `Auth()` dan `AllowOnlyGet()` di semua handler rute yang ada. Namun jelasnya ini bukan best practice. Dan juga belum tentu di tiap rute hanya ada dua validasi ini, bisa saja ada lebih banyak proses, misalnya pengecekan csrf, authorization, dan lainnya. +Jika ada banyak rute, apa yang harus kita lakukan? salah satu solusi adalah dengan memanggil fungsi `Auth()` dan `AllowOnlyGet()` di setiap handler rute yang ada. Namun jelasnya ini bukan best practice karena mengharuskan penulisan kode yang berulang-ulang. Selain itu, bisa jadi ada jenis validasi lainnya yang harus diterapkan, misalnya misalnya pengecekan csrf, authorization, dan lainnya. Maka perlu ada desain penataan kode yang lebih efisien tanpa harus menuliskan validasi yang banyak tersebut berulang-ulang. -Solusi dari masalah tersebut adalah, mengkonversi fungsi-fungsi di atas menjadi middleware. +Solusi yang pas adalah dengan membuat middleware baru untuk keperluan validasi. ## B.19.1. Interface `http.Handler` -Interface `http.Handler` merupakan tipe data paling populer di Go untuk keperluan manajemen middleware. Struct yang mengimplementasikan interface ini diwajibkan memilik method dengan skema `ServeHTTP(ResponseWriter, *Request)`. +Interface `http.Handler` merupakan tipe data paling populer di Go untuk keperluan manajemen middleware. Struct yang mengimplementasikan interface ini diwajibkan untuk memilik method dengan skema `ServeHTTP(ResponseWriter, *Request)`. -Di Go sendiri objek utama untuk keperluan routing yaitu `mux` atau multiplexer, adalah mengimplementasikan interface `http.Handler` ini. +> Di Go, objek utama untuk keperluan routing web server adalah `mux` (kependekan dari multiplexer), dan `mux` ini mengimplementasikan interface `http.Handler`. -Dengan memanfaatkan interface ini, kita akan membuat beberapa middleware. Fungsi pengecekan otentikasi dan pengecekan method akan kita ubah menjadi middleware terpisah. +Kita akan buat beberapa middleware baru dengan memanfaatkan interface `http.Handler` untuk keperluan pengecekan otentikasi dan pengecekan HTTP method. ## B.19.2. Persiapan -OK, mari kita praktekan. Pertama duplikat folder project sebelumnya sebagai folder proyek baru. Lalu pada `main.go`, ubah isi fungsi `ActionStudent` dan `main`. +OK, mari masuk ke bagian *coding*. Pertama duplikat folder project sebelumnya sebagai folder proyek baru. Lalu pada `main.go`, ubah isi fungsi `ActionStudent()` dan `main()`. - Fungsi`ActionStudent()` @@ -66,13 +72,15 @@ OK, mari kita praktekan. Pertama duplikat folder project sebelumnya sebagai fold ``` -Perubahan pada kode `ActionStudent()` adalah, pengecekan basic auth dan pengecekan method dihapus. Selain itu di fungsi `main()` juga terdapat cukup banyak perubahan, yang detailnya akan kita bahas di bawah ini. +Perubahan pada kode `ActionStudent()` adalah penghapusan kode untuk pengecekan basic auth dan HTTP method. Selain itu, di fungsi `main()` juga terdapat cukup banyak perubahan, yang detailnya akan dijelaskan sebentar lagi. ## B.19.3. Mux / Multiplexer Di Go, mux (kependekan dari multiplexer) adalah router. Semua routing pasti dilakukan lewat objek mux. -Apa benar? routing `http.HandleFunc()` sepertinya tidak menggunakan mux? Begini, sebenarnya routing tersebut juga menggunakan mux. Go memiliki default objek mux yaitu `http.DefaultServeMux`. Routing yang langsung dilakukan dari fungsi `HandleFunc()` milik package `net/http` sebenarnya mengarah ke method default mux `http.DefaultServeMux.HandleFunc()`. Agar lebih jelas, silakan perhatikan dua kode berikut. +Apa benar? Routing `http.HandleFunc()` sepertinya tidak menggunakan mux? Begini, sebenarnya routing tersebut juga menggunakan mux. Go memiliki default objek mux yaitu `http.DefaultServeMux`. Routing yang langsung dilakukan dari fungsi `HandleFunc()` milik package `net/http` sebenarnya mengarah ke method default mux `http.DefaultServeMux.HandleFunc()`. + +Agar lebih jelas perbedaannya, silakan perhatikan dua kode berikut. ```go http.HandleFunc("/student", ActionStudent) @@ -83,13 +91,13 @@ mux := http.DefaultServeMux mux.HandleFunc("/student", ActionStudent) ``` -Dua kode di atas menghasilkan hasil yang sama persis. +Dua kode di atas melakukan prosees yang ekuivalen. -Mux sendiri adalah bentuk nyata struct yang mengimplementasikan interface `http.Handler`. Untuk lebih jelasnya silakan baca dokumentasi package net/http di https://golang.org/pkg/net/http/#Handle. +Mux sendiri adalah bentuk nyata struct yang mengimplementasikan interface `http.Handler`. Di kode setelah routing, bisa dilihat objek `mux` ditampung ke variabel baru bertipe `http.Handler`. Seperti ini adalah valid karena memang struct multiplexer memenuhi kriteria interface `http.Handler`, yaitu memiliki method `ServeHTTP()`. -Kembali ke pembahasan source code. Di kode setelah routing, bisa dilihat objek `mux` ditampung ke variabel baru bertipe `http.Handler`. Seperti ini adalah valid karena memang struct multiplexer memenuhi kriteria interface `http.Handler`, yaitu memiliki method `ServeHTTP()`. +> Untuk lebih jelasnya silakan baca dokumentasi package net/http di [https://golang.org/pkg/net/http/#Handle](https://golang.org/pkg/net/http/#Handle) -Lalu dari objek `handler` tersebut, ke-dua middleware dipanggil dengan parameter adalah objek `handler` itu sendiri dan nilai baliknya ditampung pada objek yang sama. +Lalu dari objek `handler` tersebut, ke-dua middleware dipanggil dengan argument parameter diisi objek `handler` itu sendiri, dan nilai baliknya ditampung pada objek yang sama. ```go var handler http.Handler = mux @@ -97,12 +105,12 @@ handler = MiddlewareAuth(handler) handler = MiddlewareAllowOnlyGet(handler) ``` -Fungsi `MiddlewareAuth()` dan `MiddlewareAllowOnlyGet()` adalah middleware yang akan kita buat setelah ini. Cara registrasi middleware yang paling populer adalah dengan memanggilnya secara sekuensial atau berurutan, seperti pada kode di atas. +Fungsi `MiddlewareAuth()` dan `MiddlewareAllowOnlyGet()` adalah middleware yang akan kita buat sebentar lagi. Cara registrasi middleware yang paling populer adalah dengan memanggilnya secara sekuensial atau berurutan, seperti pada kode di atas. - - `MiddlewareAuth()` bertugas untuk melakukan pengencekan credentials, basic auth. - - `MiddlewareAllowOnlyGet()` bertugas untuk melakukan pengecekan method. + - `MiddlewareAuth()` bertugas melakukan pengencekan credentials, basic auth. + - `MiddlewareAllowOnlyGet()` bertugas melakukan pengecekan method. -> Silakan lihat source code beberapa library middleware yang sudah terkenal seperti gorilla, gin-contrib, echo middleware, dan lainnya; Semua metode implementasi middleware-nya adalah sama, atau paling tidak mirip. Point plus nya, beberapa di antara library tersebut mudah diintegrasikan dan compatible satu sama lain. +> Silakan lihat source code beberapa library middleware yang sudah terkenal seperti gorilla, gin-contrib, echo middleware, dan lainnya; Semua metode implementasi middleware-nya adalah sama, atau minimal mirip. Point plus nya, beberapa di antara library tersebut mudah diintegrasikan dan *compatible* satu sama lain. Kedua middleware yang akan kita buat tersebut mengembalikan fungsi bertipe `http.Handler`. Eksekusi middleware sendiri terjadi pada saat ada http request masuk. @@ -138,9 +146,9 @@ func MiddlewareAuth(next http.Handler) http.Handler { } ``` -Idealnya fungsi middleware harus mengembalikan struct yang implements `http.Handler`. Beruntungnya, Go sudah menyiapkan fungsi ajaib untuk mempersingkat pembuatan struct-yang-implemenets-`http.Handler`. Fungsi tersebut adalah `http.HandlerFunc`, cukup bungkus callback `func(http.ResponseWriter,*http.Request)` sebagai tipe `http.HandlerFunc` dan semuanya beres. +Idealnya fungsi middleware harus mengembalikan struct yang implements `http.Handler`. Beruntungnya, Go sudah menyiapkan fungsi ajaib untuk mempersingkat pembuatan struct yang implement `http.Handler`, yaitu fungsi `http.HandlerFunc()`. Cukup bungkus callback `func(http.ResponseWriter,*http.Request)` sebagai tipe `http.HandlerFunc()` maka semuanya beres. -Isi dari `MiddlewareAuth()` sendiri adalah pengecekan basic auth, sama seperti pada chapter sebelumnya. +Isi dari `MiddlewareAuth()` sendiri adalah pengecekan basic auth (sama seperti pada chapter sebelumnya). Tak lupa, ubah juga `AllowOnlyGet()` menjadi `MiddlewareAllowOnlyGet()`. @@ -167,12 +175,12 @@ Lalu test menggunakan `curl`, hasilnya adalah sama dengan pada chapter sebelumny ![Consume API](images/B_http_basic_auth_3_test_api.png) -Dibanding metode pada chapter sebelumnya, dengan teknik ini kita bisa sangat mudah mengontrol lalu lintas routing aplikasi, karena semua rute pasti melewati middleware terlebih dahulu sebelum sampai ke tujuan. Cukup maksimalkan middleware tersebut tanpa menggangu fungsi callback masing-masing rute. +Dibanding metode pada chapter sebelumnya, dengan teknik ini kita lebih mudah mengontrol lalu lintas routing aplikasi, karena semua rute pasti melewati layer middleware terlebih dahulu sebelum sampai ke handler tujuan. Cukup maksimalkan saja penerapan middleware tanpa perlu menambahkan validasi di masing-masing handler. --- diff --git a/content/B-render-html-string.md b/content/B-render-html-string.md index 6d7b3a4a5..5612c073a 100644 --- a/content/B-render-html-string.md +++ b/content/B-render-html-string.md @@ -1,6 +1,6 @@ # B.10. Template: Render HTML String -Output HTML yang muncul, selain bersumber dari template view, bisa juga bersumber dari sebuah string. Dengan menggunakan method `Parse()` milik `*template.Template` kita bisa menjadikan string html sebagai output. +Output HTML yang muncul, selain bersumber dari template view bisa juga bersumber dari sebuah string. Dengan menggunakan method `Parse()` milik `*template.Template` kita bisa menjadikan HTML string sebagai output di web. ## B.10.1. Praktek @@ -23,11 +23,11 @@ const view string = ` ` ``` -Konstanta bernama `view` bertipe `string` disiapkan, dengan isi adalah string html yang akan kita jadikan sebagai output nantinya. +Konstanta bernama `view` dengan tipe `string` disiapkan, isinya HTML string yang nanbtinya kita jadikan sebagai output pengaksesan endpoint. Kemudian buat fungsi `main()`, isinya adalah route handler `/index`. Dalam handler tersebut, string html `view` diparsing lalu dirender sebagai output. -Tambahkan juga rute `/`, yang isinya adalah me-redirect request secara paksa ke `/index` menggunakan fungsi `http.Redirect()`. +Tambahkan juga rute `/` yang isinya adalah me-redirect request secara paksa ke `/index` (via fungsi `http.Redirect()`). ```go func main() { @@ -47,9 +47,9 @@ func main() { } ``` -Pada kode di atas bisa dilihat, sebuah template bernama `main-template` disiapkan. Template tersebut diisi dengan hasil parsing string html `view` lewat method `Parse()`. +Pada kode di atas bisa dilihat sebuah template bernama `main-template` disiapkan. Template tersebut diisi dengan hasil parsing string html `view` lewat method `Parse()`. -## B.10.2. Test +## B.10.2. Testing Lakukan tes dan lihat hasilnya. @@ -58,7 +58,7 @@ Lakukan tes dan lihat hasilnya. --- diff --git a/content/B-render-specific-html-template.md b/content/B-render-specific-html-template.md index ac157e38b..d24f618c4 100644 --- a/content/B-render-specific-html-template.md +++ b/content/B-render-specific-html-template.md @@ -1,6 +1,6 @@ # B.9. Template: Render Specific HTML Template -Pada chapter ini, kita akan belajar bagaimana cara untuk render template html tertentu. Sebuah file view bisa berisikan banyak template. Template mana yang ingin di-render bisa ditentukan. +Pada chapter ini kita akan mempelajari cara render template html tertentu untuk dijadikan output pengaksesan endpoint. Sebuah file view bisa berisikan banyak template. Template mana yang ingin di-render bisa ditentukan. ## B.9.1. Front End @@ -30,11 +30,14 @@ Siapkan folder project baru, buat file template bernama `view.html`, lalu isi de {{end}} ``` -Pada file view di atas, terlihat terdapat 2 template didefinisikan dalam 1 file, template `index` dan `test`. Rencananya template `index` akan ditampilkan ketika rute `/` diakses, dan template `test` ketika rute `/test` diakses. +Pada file view di atas, terlihat terdapat 2 template didefinisikan dalam 1 file, template `index` dan `test`. + +- Template `index` ditampilkan ketika rute `/` diakses +- Template `test` ditampilkan ketika rute `/test` diakses ## B.9.2. Back End -Selanjutnya siapkan back end program, buat file `main.go`, tulis kode berikut. +Selanjutnya siapkan kode di sisi back-end, buat file `main.go`, tulis kode berikut. ```go package main @@ -63,11 +66,11 @@ func main() { } ``` -Pada kode di atas bisa dilihat, terdapat 2 rute yang masing-masing memparsing file yang sama, tapi spesifik template yang dipilih untuk di-render berbeda. +Pada kode di atas bisa dilihat, terdapat 2 rute yang masing-masing mem-parsing file yang sama, tapi spesifik template yang dipilih untuk di-render berbeda. -Contoh di rute `/`, sebuah template dialokasikan dengan nama `index`, kemudian di-parsing-lah view bernama `view.html` menggunakan method `ParseFiles()`. Golang secara cerdas akan melakukan mencari dalam file view tersebut, apakah ada template yang namanya adalah `index` atau tidak. Jika ada akan ditampilkan. Hal ini juga berlaku pada rute `/test`, jika isi dari template bernama `test` akan ditampilkan tiap kali rute tersebut diakses. +Contoh di rute `/`, sebuah template dialokasikan dengan nama `index`, kemudian di-parsing-lah view bernama `view.html` menggunakan method `ParseFiles()`. Go secara cerdas melakukan pencarian dalam file view tersebut, apakah ada template yang namanya adalah `index` atau tidak. Jika ada maka ditampilkan. Hal ini juga berlaku pada rute `/test`, jika isi dari template bernama `test` ada maka ditampilkan. -## B.9.3. Test +## B.9.3. Testing Lakukan tes pada program yang telah kita buat, kurang lebih hasilnya seperti pada gambar berikut. @@ -76,7 +79,7 @@ Lakukan tes pada program yang telah kita buat, kurang lebih hasilnya seperti pad --- diff --git a/content/B-routing-http-handlefunc.md b/content/B-routing-http-handlefunc.md index 9df6cd14b..16d1de7cf 100644 --- a/content/B-routing-http-handlefunc.md +++ b/content/B-routing-http-handlefunc.md @@ -1,21 +1,18 @@ # B.2. Routing `http.HandleFunc` -Dalam Go, routing bisa dilakukan dengan beberapa cara, di antaranya: +Routing di Go bisa dilakukan dengan beberapa cara, di antaranya: 1. Dengan memanfaatkan fungsi `http.HandleFunc()` 2. Mengimplementasikan interface `http.Handler` pada suatu struct, untuk kemudian digunakan pada fungsi `http.Handle()` 3. Membuat multiplexer sendiri dengan memanfaatkan struct `http.ServeMux` - 4. Dan lainnya -Pada buku ini, semua cara tersebut akan dibahas, namun khusus pada chapter ini saja, hanya `http.HandleFunc()` yang kita pelajari. +Pada buku ini, semua cara tersebut akan dibahas, namun khusus di chapter ini hanya `http.HandleFunc()` yang kita pelajari. -> Metode routing cara pertama dan cara kedua memiliki kesamaan yaitu sama-sama menggunakan `DefaultServeMux` untuk pencocokan rute/endpoint yang diregistrasikan. Mengenai apa itu `DefaultServeMux` akan kita bahas lebih mendetail pada chapter lain. +> Metode routing cara pertama dan cara kedua memiliki kesamaan yaitu sama-sama menggunakan `DefaultServeMux` sebagai router. Mengenai apa itu `DefaultServeMux` akan kita bahas lebih mendetail pada chapter lain. ## B.2.1. Penggunaan `http.HandleFunc()` -Seperti yang sudah dijelaskan sekilas pada chapter sebelumnya, fungsi `http.HandleFunc()` digunakan untuk registrasi rute/endpoint dan handler-nya. - -Penggunaan fungsi ini cukup mudah, panggil saja fungsi lalu isi dua parameternya. +Seperti yang sudah dijelaskan sekilas pada chapter sebelumnya, fungsi `http.HandleFunc()` digunakan untuk registrasi rute/endpoint beserta handler-nya. Penggunaan fungsi ini cukup mudah, panggil saja fungsi lalu isi dua parameternya. 1. Parameter ke-1, adalah rute (atau endpoint). Sebagai contoh: `/`, `/index`, `/about`. 2. Parameter ke-2, berisikan handler untuk rute bersangkutan. Sebagai contoh handler untuk rute `/` bertugas untuk menampilkan output berupa html `

      hello

      `. @@ -29,7 +26,7 @@ import "fmt" import "net/http" ``` -Buat fungsi `main()`, di dalamnya siapkan sebuah closure `handlerIndex`, lalu gunakan closure tersebut sebagai handler dari dua rute baru yang diregistrasi, yaitu `/` dan `/index`. +Buat fungsi `main()`, di dalamnya siapkan sebuah closure `handlerIndex`, lalu gunakan closure tersebut sebagai handler dari dua rute baru yang sebentar lagi disiapkan, yaitu rute `/` dan `/index`. ```go func main() { @@ -42,7 +39,7 @@ func main() { } ``` -Selanjutnya, masih dalam fungsi `main()`, tambahkan rute baru `/data` dengan handler adalah anonymous function. +Selanjutnya, masih dalam fungsi `main()`, tambahkan rute `/data` dengan handler adalah anonymous function. ```go func main() { @@ -54,7 +51,7 @@ func main() { } ``` -Terakhir, jalankan server. +Terakhir, jalankan web server. ```go func main() { @@ -71,12 +68,12 @@ Tes dan lihat hasilnya. ![Rute `/data` mengembalikan data json](images/B_routing_http_handlefunc_1_routing.png) -Dalam routing, handler bisa berupa fungsi, closure, ataupun anonymous function; bebas, yang terpenting adalah skema fungsi-nya sesuai dengan `func (http.ResponseWriter, *http.Request)`. +Handler bisa berupa fungsi, closure, ataupun anonymous function, intinya bebas, yang terpenting adalah skema fungsi-nya harus sesuai dengan `func (http.ResponseWriter, *http.Request)`. --- diff --git a/content/B-routing-static-assets.md b/content/B-routing-static-assets.md index 1254b354a..ceb7bea80 100644 --- a/content/B-routing-static-assets.md +++ b/content/B-routing-static-assets.md @@ -1,6 +1,8 @@ # B.3. Routing Static Assets -Pada bagian ini kita akan belajar bagaimana cara routing static assets atau static contents. Seperti file css, js, gambar, umumnya dikategorikan sebagai static assets. +Pada bagian ini kita akan mempelajari cara routing static assets / static contents. Static assets yang dimaksud adalah seperti file statis css, js, gambar, dan lainnya. + +Ok, mari belajar sambil praktek. ## B.3.1. Struktur Aplikasi @@ -8,7 +10,7 @@ Buat project baru, siapkan file dan folder dengan struktur sesuai dengan gambar ![Structure](images/B_routing_static_assets_1_structure.png) -Dalam folder `assets`, isi dengan file apapun, bisa gambar atau file js. Selanjutnya masuk ke bagian routing static assets. +Dalam folder `assets`, isi dengan file apapun bebas, bisa gambar atau file js. Selanjutnya masuk ke bagian routing static assets. ## B.3.2. Routing @@ -30,13 +32,13 @@ func main() { } ``` -Syarat yang dibutuhkan untuk routing static assets masih sama dengan routing handler, yaitu perlu didefiniskan rute-nya dan handler-nya. Hanya saja pembedanya, dalam routing static assets yang digunakan adalah `http.Handle()`, bukan `http.HandleFunc()`. +Syarat yang dibutuhkan untuk routing static assets masih sama dengan routing handler, yaitu perlu didefiniskan rute beserta handler-nya. Hanya saja pembedanya di sini adalah dalam routing static assets yang digunakan adalah `http.Handle()`, bukan `http.HandleFunc()`. 1. Rute terpilih adalah `/static/`, maka nantinya semua request yang di awali dengan `/static/` akan diarahkan ke sini. Registrasi rute menggunakan `http.Handle()` adalah berbeda dengan routing menggunakan `http.HandleFunc()`, lebih jelasnya akan ada sedikit penjelasan pada chapter lain. 2. Sedang untuk handler-nya bisa di-lihat, ada pada parameter ke-2 yang isinya statement `http.StripPrefix()`. Sebenarnya actual handler nya berada pada `http.FileServer()`. Fungsi `http.StripPrefix()` hanya digunakan untuk membungkus actual handler. -Fungsi `http.FileServer()` mengembalikan objek ber-tipe `http.Handler`. Fungsi ini berguna untuk men-serve semua http request, dengan konten yang didefinisikan pada parameter. Pada konteks ini yang di-maksud adalah `http.Dir("assets")`. Semua konten, entah file ataupun folder, yang ada di dalam folder `assets` akan di proses dalam handler. +Fungsi `http.FileServer()` mengembalikan objek ber-tipe `http.Handler`. Fungsi ini berguna untuk merespon http request dengan konten yang ada di dalam folder `assets` sesuai permintaan. Jalankan `main.go`, lalu test hasilnya di browser `http://localhost:9000/static/`. @@ -57,36 +59,36 @@ Jika dilihat pada struktur folder yang sudah di-buat, di dalam folder `assets` t * Request ke `/some/folder/test.png` mengarah path `./some/folder/test.png` relatif dari folder `assets` * ... dan seterusnya -> Fungsi `http.Dir()` berguna untuk adjustment path parameter. Separator dari path yang di-definisikan akan otomatis di-konversi ke path separator sesuai sistem operasi. +> Fungsi `http.Dir()` berguna untuk *adjustment path parameter*. Separator dari path yang di-definisikan otomatis di-konversi ke path separator sesuai sistem operasi. -Contoh selanjutnya, silakan perhatikan kode berikut. +Sekarang coba perhatikan kode berikut. ```go http.Handle("/static", http.FileServer(http.Dir("assets"))) ``` -Hasil dari routing: +Dengan skema routing di atas, maka: * Request ke `/static/site.css` mengarah ke `./static/site.css` relatif dari folder `assets` * Request ke `/static/script.js` mengarah ke `./static/script.js` relatif dari folder `assets` * Request ke `/static/some/folder/test.png` mengarah ke `./static/some/folder/test.png` relatif dari folder `assets` * ... dan seterusnya -Terlihat bahwa rute yang didaftarkan juga akan digabung dengan path destinasi file yang dicari, dan ini menjadikan path tidak valid. File `site.css` berada pada path `assets/site.css`, sedangkan dari routing di atas pencarian file mengarah ke path `assets/static/site.css`. Di sinilah kegunaan dari fungsi `http.StripPrefix()`. +Bisa dilihat bahwa rute yang didaftarkan juga akan digabung dengan path destinasi file yang dicari, dan ini menjadikan path tidak valid. File `site.css` berada pada path `assets/site.css`, sedangkan dari routing di atas pencarian file mengarah ke path `assets/static/site.css`. Di sinilah kegunaan dari fungsi `http.StripPrefix()`. -Fungsi `http.StripPrefix()` ini berguna untuk menghapus prefix dari endpoint yang di-request. Pada contoh paling atas, request ke url yang di awali dengan `/static/` hanya akan di ambil url setelahnya. +Fungsi `http.StripPrefix()` berguna untuk menghapus prefix dari endpoint yang diakses. Request ke URL yang di awali dengan `/static/` akan diambil informasi endpoint-nya tanpa prefix `/static/`. - * Request ke `/static/site.css` menjadi `/site.css` - * Request ke `/static/script.js` menjadi `/script.js` - * Request ke `/static/some/folder/test.png` menjadi `/some/folder/test.png` + * Request ke `/static/site.css` mengarah ke `site.css` + * Request ke `/static/script.js` mengarah ke `script.js` + * Request ke `/static/some/folder/test.png` mengarah ke `some/folder/test.png` * ... dan seterusnya -Routing static assets menjadi valid, karena file yang di-request akan cocok dengan path folder dari file yang di request. +Dengan penerapan `http.StripPrefix()` maka routing static assets menjadi valid, karena file yang di-request akan cocok dengan path folder yang telah dibuat. --- diff --git a/content/B-server-handler-http-request-cancellation.md b/content/B-server-handler-http-request-cancellation.md index 0ef2ee697..83348a529 100644 --- a/content/B-server-handler-http-request-cancellation.md +++ b/content/B-server-handler-http-request-cancellation.md @@ -1,22 +1,20 @@ # B.23. Server Handler HTTP Request Cancellation -Dalam konteks web application, kadang kala sebuah http request butuh waktu cukup lama untuk selesai, bisa jadi karena kode yang kurang dioptimasi, atau prosesnya memang lama, atau mungkin faktor lainnya. Dari sisi client, biasanya ada handler untuk cancel request jika melebihi batas timeout yang sudah didefinisikan, dan ketika itu terjadi di client akan sangat mudah untuk antisipasinya. +Dalam konteks web application, kadang kala sebuah HTTP request butuh waktu cukup lama untuk selesai, bisa jadi karena kode yang kurang dioptimasi atau prosesnya memang lama, atau mungkin ada faktor lainnya. Dari sisi client, biasanya ada handler untuk cancel request ketika request melebihi batas timeout yang sudah ditentukan. -Berbeda dengan handler di back end-nya, by default request yang sudah di-cancel oleh client tidak terdeteksi (proses di back end akan tetap lanjut). Umumnya tidak ada masalah mengenai ini, tapi ada kalanya kita perlu men-treat cancelled request dengan baik untuk keperluan lain (logging, atau lainnya). +Berbeda dengan handler di back end-nya, by default request yang sudah di-cancel oleh client tidak mempengaruhi yang terjadi di back-end, proses di back end akan tetap lanjut hingga selesai. Umumnya hal ini bukan merupakan masalah, tapi untuk beberapa *case* ada baiknyakita perlu men-*treat* *cancelled request* dengan baik. Dan pada chapter ini kita akan belajar caranya. -Pada chapter ini kita akan belajar caranya. - -> Chapter ini fokus terhadap cancellation pada client http request. Untuk cancellation pada proses konkuren silakan merujuk ke [A.64. Concurrency Pattern: Context Cancellation Pipeline](/A-pipeline-context-cancellation.html). +> Chapter ini fokus terhadap cancellation pada client http request di sisi back-end. Untuk topik cancellation pada proses konkuren silakan pembahasannya ada di chapter [A.64. Concurrency Pattern: Context Cancellation Pipeline](/A-pipeline-context-cancellation.html). ## B.32.1. Praktek -Dari objek `*http.Request` bisa diambil objek context lewat method `.Context()`, dan dari context tersebut kita bisa mendeteksi apakah sebuah request di-cancel atau tidak oleh client. +Dari objek `*http.Request` informasi objek context bisa diakses lewat method `.Context()`, dan dari context tersebut kita bisa mendeteksi apakah sebuah request di-cancel atau tidak oleh client. -> Pada chapter ini kita tidak membahas secara rinci apa itu context. Silakan langsung merujuk ke chapter [D.2. Google API Search Dengan Timeout](/D-google-api-search.html) untuk lebih detailnya. +> Pada chapter ini kita tidak membahas secara rinci apa itu context karena sudah ada pembahasan terpisah mengenai topik tersebut di chapter [A.64. Concurrency Pattern: Context Cancellation Pipeline](/A-pipeline-context-cancellation.html). -Object context memiliki method `.Done()` yang nilai baliknya berupa channel. Dari channel tersebut kita bisa deteksi apakah request di-cancel atau tidak, caranya dengan cara mengecek apakah ada data yang terkirim lewat channel tersebut, jika ada maka lakukan pengecekan pada error message-nya, jika ada keterangan `"cancelled"` maka diasumsikan request tersebut dibatalkan. +Object context memiliki method `.Done()` yang nilai baliknya berupa channel. Dari channel tersebut kita bisa deteksi apakah request di-cancel atau tidak oleh client, jika ada data yang diterima via channel tersebut dan error yang didapat ada keterangan `"cancelled"` maka bisa diasumsikan request tersebut dibatalkan oleh client. -Mari kita praktekan langsung. Siapkan base kode sederhana berikut. +Mari kita praktekan langsung. Silakan mulai dengan menulis kode berikut. ```go package main @@ -39,9 +37,11 @@ func main() { } ``` -Di dalam `handleIndex()` disimulasikan sebuah proses membutuhkan waktu lama untuk selesai (kita gunakan `time.Sleep()` untuk ini). Umumnya kode akan dituliskan langsung dalam handler tersebut, tapi pada kasus ini tidak. Untuk bisa mendeteksi sebuah request di-cancel atau tidak, harus di-dispatch sebuah goroutine baru. +Di dalam `handleIndex()` disimulasikan sebuah proses membutuhkan waktu lama untuk selesai (kita gunakan `time.Sleep()` untuk ini). Umumnya kode dituliskan langsung dalam handler tersebut, tapi pada kasus ini tidak. Untuk bisa mendeteksi sebuah request di-cancel atau tidak kita akan manfaatkan goroutine baru. + +Dalam penerapannya ada dua pilihan opsi: -- Cara ke-1: bisa dengan menaruh proses utama di dalam gorutine tersebut, dan menaruh kode untuk deteksi di luar (di dalam handler-nya). +- Cara ke-1: Dengan menaruh proses utama di dalam gorutine tersebut, dan menaruh kode untuk deteksi di luar (di dalam handler-nya). - Cara ke-2: Atau sebaliknya. Menaruh proses utama di dalam handler, dan menempatkan deteksi cancelled request dalam goroutine baru. Pada contoh berikut, kita gunakan cara pertama. Tulis kode berikut dalam handler. @@ -70,14 +70,14 @@ case <-done: } ``` -Pada kode di atas terlihat, proses utama dibungkus dalam goroutine. Ketika selesai, sebuah data dikirimkan ke channel `done`. +Pada kode di atas terlihat, proses utama dibungkus dalam goroutine. Ketika selesai, maka back-end akan menerima data via channel `done`. -Lalu diluar, keyword `select` dipergunakan untuk deteksi pengiriman terhadap dua channel. +Keyword `select` di situ disiapkan untuk pendeteksian dua kondisi berikut: -- Channel `r.Context().Done()`, jika channel ini menerima data maka diasumsikan request selesai. Selanjutnya lakukan pengecekan pada objek error milik konteks untuk deteksi apakah selesai-nya request ini karena memang selesai, atau di-cancel oleh client, atau faktor lainnya. -- Channel `<-done`, jika channel ini menerima data, maka proses utama adalah selesai. +- Channel `r.Context().Done()`. Jika channel ini menerima data maka diasumsikan request selesai. Selanjutnya lakukan pengecekan pada objek error milik context untuk deteksi apakah selesai-nya request ini karena memang selesai, atau di-cancel oleh client, atau faktor lainnya. +- Channel `<-done`. Jika channel ini menerima data, maka proses utama adalah selesai. -Jalankan kode lalu test hasilnya. +Sekarang coba jalankan kode lalu test hasilnya. ```bash curl -X GET http://localhost:8080/ @@ -91,7 +91,9 @@ Pada gambar di atas terdapat dua request, yg pertama sukses dan yang kedua adala ## B.32.2. Handle Cancelled Request yang ada Payload-nya -Khusus untuk request dengan HTTP method yang mewajibkan untuk ada request body-nya (payload), maka channel `r.Context().Done()` tidak akan menerima data hingga terjadi proses read pada body payload. +Khusus untuk request dengan HTTP method yang memiliki request body (payload), maka channel `r.Context().Done()` tidak akan menerima data hingga terjadi proses read pada body payload. + +Silakan coba saja, misalnya dengan menambahkan kode berikut. ```go go func() { @@ -107,7 +109,7 @@ go func() { }() ``` -Jalankan ulang program kemudian test. +Hasilnya: ```go curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}' @@ -118,7 +120,7 @@ curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}' --- diff --git a/content/B-simple-configuration.md b/content/B-simple-configuration.md index 223e19d31..699070a4f 100644 --- a/content/B-simple-configuration.md +++ b/content/B-simple-configuration.md @@ -1,12 +1,12 @@ # B.22. Simple Configuration -Dalam development, pasti banyak sekali variabel dan konstanta yang diperlukan. Mulai dari variabel yang dibutuhkan untuk start server seperti port, timeout, hingga variabel global dan variabel shared lainnya. +Dalam development, pastinya kita programmer akan berurusan dengan banyak sekali variabel dan konstanta untuk keperluan konfigurasi. Misalnya, variabel berisi informasi port web server, timeout, variabel global, dan lainnya. -Pada chapter ini, kita akan belajar cara membuat config file modular. +Pada chapter ini, kita akan belajar dasar pengelolahan variabel konfigurasi dengan memanfaatkan file JSON. ## B.22.1. Struktur Aplikasi -Pertama-tama, buat project baru, siapkan dengan struktur seperti gambar berikut. +Pertama-tama, buat project baru dengan struktur seperti gambar berikut. ![Structure](images/B_simple_configuration_1_structure.png) @@ -17,7 +17,7 @@ Folder `conf` berisi 2 file. ## B.22.2. File Konfigurasi JSON `config.json` -Semua konfigurasi perlu dituliskan dalam file ini. Desain struktur JSON nya untuk bisa mudah dipahami. Tulis data berikut di file tersebut. +Semua konfigurasi dituliskan dalam file ini. Desain struktur JSON-nya untuk bisa mudah dipahami, contoh: ```json { @@ -33,7 +33,7 @@ Semua konfigurasi perlu dituliskan dalam file ini. Desain struktur JSON nya untu } ``` -Ada 4 buah konfigurasi disiapkan. +Data JSON di atas berisi 4 buah data konfigurasi. 1. Property `server.port`. Port yang digunakan saat start web server. 2. Property `server.read_timeout`. Dijadikan sebagai timeout read. @@ -42,7 +42,7 @@ Ada 4 buah konfigurasi disiapkan. ## B.22.3. Pemrosesan Konfigurasi -Pada file `config.go`, nantinya kita akan buat sebuah fungsi, isinya mengembalikan objek cetakan struct representasi dari `config.json`. +Pada file `config.go` kita akan siapkan sebuah fungsi yang isinya mengembalikan objek cetakan struct didapat dari konten file `config.json`. Siapkan struct nya terlebih dahulu. @@ -71,14 +71,13 @@ type _Configuration struct { } ``` -Bisa dilihat pada kode di atas, struct bernama `_Configuration` dibuat. Struct ini berisikan banyak property yang strukturnya sama persis dengan isi file `config.json`. Dengan desain seperti ini, akan sangat memudahkan developer dalam pengaksesan konfigurasi. +Bisa dilihat pada kode di atas, struct bernama `_Configuration` dibuat. Struct ini berisikan banyak property yang strukturnya sama persis dengan isi file `config.json`. Dengan skema seperti itu akan cukup mempermudah developer dalam pengaksesan data konfigurasi. -Dari struct tersebut tercetak private objek bernama `shared`. Variabel inilah yang nantinya akan dikembalikan lewat fungsi yang akan kita buat. +Dari struct tersebut disiapkan objek bernama `shared`. Variabel ini berisi informasi konfigurasi hasil baca `config.json`, dan nantinya isinya bisa diakses via fungsi fungsi yang sebentar lagi akan dibuat. -Selanjutnya, isi `init()` dengan beberapa proses: membaca file json, lalu di decode ke object `shared`. - -Dengan menuliskan proses barusan ke fungsi `init()`, pada saat package `conf` ini di import ke package lain maka file `config.json` akan otomatis di parsing. Dan dengan menambahkan sedikit validasi, parsing hanya akan terjadi sekali di awal. +Selanjutnya, siapkan fungsi `init()` dengan isi operasi baca file `config.json` serta operasi decode data JSON dari isi file tersebut ke variabel `shared`. +Dengan adanya fungsi `init()` maka pada saat package `conf` ini di-import ke package lain otomatis file `config.json` dibaca dan di-parse untuk disimpan di variabel `shared`. Tambahkan juga validasi untuk memastikan kode hanya di-parse sekali saja. ```go func init() { @@ -107,7 +106,7 @@ func init() { } ``` -Lalu buat fungsi yang mengembalikan object `shared`. +Kemudian buat fungsi `Configuration()` yang isinya menjembatani pengaksesan object `shared`. ```go func Configuration() _Configuration { @@ -145,7 +144,7 @@ func (c CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { Bisa dilihat dalam method `ServeHTTP()` di atas, ada pengecekan salah satu konfigurasi, yaitu `Log.Verbose`. Cara pengaksesannya cukup mudah, yaitu lewat fungsi `Configuration()` milik package `conf` yang telah di-import. -OK, kembali lagi ke contoh, dari mux di atas, buat object baru bernama `router`, lalu lakukan registrasi beberapa rute. +OK, kembali lagi ke contoh, dari mux di atas dibuatkan object baru bernama `router`, lalu beberapa rute didaftarkan ke object mux tersebut. ```go func main() { @@ -161,8 +160,7 @@ func main() { } ``` -Selanjutnya, kita akan start web server untuk serve mux di atas. Masih di dalam `main.go`, tambahkan kode berikut. - +Selanjutnya, siapkan kode untuk start web server. Tulis kode berikut di dalam fungsi `main()` tepat setelah kode deklarasi route handler. ```go server := new(http.Server) @@ -181,18 +179,18 @@ if err != nil { } ``` -Objek baru bernama `server` telah dibuat dari struct `http.Server`. Untuk start server cukup panggil method `ListenAndServe()` milik objek tersebut. +Di atas, ada objek baru dibuat dari struct `http.Server`, yaitu `server`. Untuk start server, panggil method `ListenAndServe()` milik objek tersebut. Dengan memanfaatkan struct ini, kita bisa meng-custom beberapa konfigurasi default pada Go web server. Di antaranya seperti `ReadTimeout` dan `WriteTimeout`. -Pada kode di atas bisa kita lihat, ada 4 buah properti milik `server` di-isi. +Bisa dilihat di contoh ada 4 buah properti milik `server` yang diisi nilainya dengan data konfigurasi. - `server.Handler`. Properti ini wajib di isi dengan custom mux yang dibuat. - `server.ReadTimeout`. Adalah timeout ketika memproses sebuah request. Kita isi dengan nilai dari configurasi. - `server.WriteTimeout`. Adalah timeout ketika memproses response. - `server.Addr`. Port yang digunakan web server pada saat start. -Terakhir jalankan aplikasi, akses dua buah endpoint yang sudah dibuat, lalu coba cek di console. +Ok. Sekarang jalankan aplikasi, akses dua buah endpoint yang sudah dibuat, kemudian cek di console. ![Structure](images/B_simple_configuration_2_log.png) @@ -200,25 +198,25 @@ Coba ubah konfigurasi pada `config.json` nilai `log.verbose` menjadi `false`. La ## B.22.5. Kekurangan Konfigurasi File -Ok, kita telah selesai belajar tentang cara membuat file konfigurasi yang mudah dibaca dan praktis. Namun penerapan kontrol konfigurasi dengan metode ini kurang dianjurkan karena beberapa hal: +Ok, kita telah selesai belajar tentang cara membuat file konfigurasi yang terpusat dan mudah dibaca. Metode konfigurasi seperti ini umum digunakan, tapi dalam penerapannya memiliki beberapa *cons* yang mungkin akan mulai terasa ketika aplikasi arsitektur aplikasi berkembang dan arsitektur sistemnya menjadi kompleks. *Cons* yang dimaksud diantaranya adalah: #### ◉ Tidak mendukung komentar -Komentar sangat penting karena untuk aplikasi besar yang konfigurasi item-nya sangat banyak - akan susah untuk dipahami. Sebenarnya perihal ini bisa di-*resolve* menggunakan jenis konfigurasi lain seperti `YAML`, `.env`, atau lainnya. +Komentar sangat penting karena untuk aplikasi besar yang konfigurasi item-nya sangat banyak, konfigurasi seperti pada contoh ini akan cukup susah untuk dikelola. Sebenarnya masalah ini bisa diselesaikan dengan mudah dengan cara mengadopsi file format lainnya, misalnya `YAML`, `.env`, atau lainnya. #### ◉ Nilai konfigurasi harus diketahui di awal -Kita harus tau semua value tiap-tiap konfigurasi terlebih dahulu, dan dituliskan ke file, sebelum aplikasi di-up. Dari sini akan sangat susah jika misal ada beberapa konfigurasi yang kita tidak tau nilainya tapi tau cara pengambilannya. +Kita harus tau semua value tiap-tiap konfigurasi terlebih dahulu sebelum dituliskan ke file, dan sebelum aplikasi di-up. Dari sini akan sangat susah jika misal ada beberapa konfigurasi yang kita tidak tau nilainya tapi tau cara pengambilannya. -Contohnya pada beberapa kasus, seperti di AWS, database server yang di-setup secara automated akan meng-generate connection string yang host-nya bisa berganti-ganti tiap start-up, dan tidak hanya itu, bisa saja username, password dan lainnya juga tidak statis. +Contohnya seperti ini, di beberapa kasus, misalnya di AWS, database server yang di-setup secara automated akan meng-generate connection string yang host-nya bisa berganti-ganti tiap start-up, dan tidak hanya itu saja, bisa saja username, password dan lainnya juga tidak statis. -Dengan ini akan sangat susah jika kita harus cari terlebih dahulu value konfigurasi tersebut untuk kemudian dituliskan ke file. Memakan waktu dan kurang baik dari banyak sisi. +Dengan ini akan cukup merepotkan jika kita harus cari terlebih dahulu value konfigurasi tersebut untuk kemudian dituliskan ke file secara manual. #### ◉ Tidak terpusat -Dalam pengembangan aplikasi, banyak konfigurasi yang nilai-nya akan didapat lewat jalan lain, seperti *environment variables* atau *command arguments*. +Dalam pengembangan aplikasi, banyak konfigurasi yang nilai-nya akan didapat lewat jalan lain, seperti *environment variables* atau *command arguments*. Menyimpan konfigurasi file itu sudah cukup bagus, cuman untuk *case* dimana terdapat banyak sekali services, agak merepotkan pengelolahannya. -Akan lebih mudah jika hanya ada satu sumber konfigurasi saja untuk dijadikan acuan. +Ketika ada perubahan konfigurasi, semua services harus direstart. #### ◉ Statis (tidak dinamis) @@ -228,13 +226,12 @@ Hal tersebut memiliki beberapa konsekuensi, untuk aplikasi yang di-manage secara #### ◉ Solusi -Kita akan membahas solusi dari beberapa masalah di atas pada chapter terpisah, yaitu [Best Practice Configuration Menggunakan Environment Variable -](/C-best-practice-configuration-env-var) +Kita akan membahas solusi dari beberapa masalah di atas (tidak semuanya) pada chapter terpisah, yaitu [C.11. Best Practice Configuration Menggunakan Environment Variable](/C-best-practice-configuration-env-var.html) --- diff --git a/content/B-template-actions-variables.md b/content/B-template-actions-variables.md index a7b7c3876..042cff34d 100644 --- a/content/B-template-actions-variables.md +++ b/content/B-template-actions-variables.md @@ -1,13 +1,13 @@ # B.6. Template: Actions & Variables -[**Actions**](https://golang.org/pkg/text/template/#hdr-Actions) adalah *predefined* keyword yang sudah disediakan Go, biasa dimanfaatkan dalam pembuatan template. +[**Actions**](https://golang.org/pkg/text/template/#hdr-Actions) merupakan *predefined* keyword yang disediakan oleh Go. Actions biasa dimanfaatkan dalam pembuatan template. Sebenarnya pada dua chapter sebelumnya, secara tidak sadar kita telah menggunakan beberapa jenis actions, di antaranya: - Penggunaan **pipeline output**. Nilai yang diapit tanda \{\{ \}\}, yang nantinya akan dimunculkan di layar sebagai output, contohnya: \{\{"hello world"\}\}. - Include template lain menggunakan keyword `template`, contohnya: \{\{template "name"\}\}. -Pada chapter ini, kita akan belajar lebih banyak lagi tentang actions lain yang disediakan Go, juga cara pembuatan dan pemanfaatan variabel pada template view. +Pada chapter ini, kita akan belajar lebih banyak lagi tentang actions lain, juga cara pembuatan dan pemanfaatan variabel pada template view. ## B.6.1. Persiapan @@ -33,7 +33,7 @@ type Person struct { } ``` -Pada kode di atas, dua buah struct disiapkan, `Info` dan `Person` (yang di mana struct `Info` di-embed ke dalam struct `Person`). Kedua struct tersebut nantinya akan digunakan untuk pembuatan objek, yang kemudian object tersebut disisipkan ke dalam view. +Pada kode di atas, dua buah struct disiapkan, `Info` dan `Person` (yang mana struct `Info` di-embed ke dalam struct `Person`). Kedua struct tersebut nantinya akan digunakan untuk pembuatan objek untuk kemudian disisipkan ke dalam view. Selanjutnya, siapkan fungsi `main()`, dengan di dalamnya berisikan 1 buah route handler `/`, dan juga kode untuk menjalankan server pada port `9000`. @@ -58,9 +58,9 @@ func main() { } ``` -Pada route handler `/` di atas, variabel objek `person` dibuat, lalu disisipkan sebagai data pada view `view.html` yang sebelumya sudah diparsing. +Pada route handler `/` di atas, variabel objek `person` dibuat, kemudian disisipkan sebagai data pada view `view.html`. -Perlu diketahui, ketika data yang disisipkan ke view berbentuk `map`, maka `key` (yang nantinya akan menjadi nama variabel) boleh dituliskan dalam huruf kecil. Sedangkan jika berupa variabel objek `struct`, maka property harus dituliskan public (huruf pertama kapital). +Perlu diketahui, ketika data yang disisipkan ke view bertipe `map`, maka `key` (yang nantinya akan menjadi nama variabel) boleh dituliskan dalam huruf kecil. Sedangkan jika berupa variabel objek `struct`, maka property harus dituliskan public (huruf pertama kapital). > Data yang disisipkan ke view, jika tipe nya adalah struct, maka hanya properties ber-modifier public (ditandai dengan huruf kapital di awal nama property) yang bisa diakses dari view. @@ -82,7 +82,7 @@ Selanjutnya silakan ikuti step-step berikut. ## B.6.2. Pipeline Output & Komentar -Actions pertama yang akan kita coba terapkan adalah pipeline output, menampilkan output ke layar. Caranya cukup mudah, cukup dengan menuliskan apa yang ingin ditampilkan di layar dengan diapit tanda `{{ }}` (bisa berupa variabel yang dilempar dari back end, bisa juga literal string). +Actions pertama yang akan kita coba terapkan adalah pipeline output, menampilkan output ke layar. Caranya cukup mudah, dengan menuliskan apa yang ingin ditampilkan di layar dengan diapit tanda `{{ }}` (bisa berupa variabel yang dilempar dari back end, bisa juga literal string). Tulis kode berikut di dalam tag `
` pada `view.html`. @@ -135,7 +135,7 @@ Actions `range` digunakan untuk melakukan perulangan pada template view. Keyword ``` -Penulisannya cukup unik, keyword `range` dituliskan terlebih dahulu, diikuti variabel penampung index dan elemen. Jika yang dibutuhkan hanya elemen saja, bisa cukup gunakan `{{range $elem := .Hobbies}}`. Semua kode setelah baris deklarasi hingga penutup `{{end}}`, akan diulang sesuai jumlah elemen/item-nya. +Penulisannya cukup unik, keyword `range` dituliskan terlebih dahulu, diikuti variabel penampung index dan elemen. Jika yang dibutuhkan hanya elemen saja, maka gunakan `{{range $elem := .Hobbies}}`. Semua kode setelah baris deklarasi hingga penutup `{{end}}`, akan diulang sesuai jumlah elemen/item-nya. ![Perulangan](images/B_template_actions_variables_3_loop.png) @@ -175,7 +175,7 @@ Lalu bagaimana cara pengaksesan method yang membutuhkan parameter, jika tanda ku ## B.6.6. Penggunaan Keyword `with` Untuk Mengganti Scope Variabel Pada Suatu Blok -Secara default **current scope** di template view adalah data yang dilempar back end. Scope current objek bisa diganti dengan menggunakan keyword `with`, sehingga nantinya untuk mengakses sub-property variabel objek (seperti `.Info.Affiliation`), bisa tidak dilakukan dari objek terluar. +Default-nya, **current scope** di template view adalah data yang dilempar back end. Scope current objek bisa diganti dengan menggunakan keyword `with`, sehingga nantinya untuk mengakses sub-property variabel objek (seperti `.Info.Affiliation`), bisa tidak dilakukan dari objek terluar. > Current scope yg dimaksud di sini adalah seperti object `this` ibarat bahasa pemrograman lain. @@ -247,7 +247,7 @@ Untuk seleksi kondisi yang kondisinya adalah bersumber dari variabel bertipe `bo --- diff --git a/content/B-template-custom-functions.md b/content/B-template-custom-functions.md index 415c82267..cf7281ef4 100644 --- a/content/B-template-custom-functions.md +++ b/content/B-template-custom-functions.md @@ -1,6 +1,6 @@ # B.8. Template: Custom Functions -Pada chapter sebelumnya kita telah mengenal beberapa *predefined* function yang disediakan oleh Go. Kali ini kita akan belajar tentang fungsi custom, bagaimana cara membuat dan menggunakannya dalam template. +Pada chapter sebelumnya kita telah berkenalan dengan beberapa *predefined* function yang disediakan oleh Go. Kali ini kita akan belajar tentang fungsi custom, bagaimana cara pembuatan dan penggunaannya dalam template. ## B.8.1. Front End @@ -20,14 +20,14 @@ Pertama, siapkan project baru. Buat file template `view.html`, lalu isi dengan k ``` -Ada 2 hal yang perlu diperhatikan dari kode di atas. Pertama, terdapat dua buah fungsi yang dipanggil beberapa kali. +Ada 2 hal yang perlu diperhatikan dari kode di atas. Terdapat dua buah fungsi yang dipanggil beberapa kali. 1. Fungsi `unescape()`, digunakan untuk menampilkan string tanpa di-escape 2. Fungsi `avg()`, digunakan untuk mencari rata-rata dari angka-angka yang disisipkan sebagai parameter Kedua fungsi tersebut adalah fungsi kustom yang akan kita buat. -Hal ke-2, terdapat 1 baris statement yang penulisannya agak unik, yaitu `{{"" | unescape}}`. Statement tersebut maknanya adalah string `""` digunakan sebagai parameter dalam pemanggilan fungsi `unescape`. Tanda pipe atau `|` adalah penanda bahwa parameter dituliskan terlebih dahulu sebelum nama fungsi nya. +Di contoh terdapat 1 baris statement yang penulisannya agak unik, yaitu `{{"" | unescape}}`. Statement tersebut maknanya adalah string `""` digunakan sebagai parameter dalam pemanggilan fungsi `unescape`. Tanda pipe atau `|` adalah penanda bahwa parameter dituliskan terlebih dahulu sebelum nama fungsi-nya. ## B.8.2. Back End @@ -62,7 +62,7 @@ var funcMap = template.FuncMap{ Dalam `funcMap` di atas, dua buah fungsi disiapkan, `unescape()` dan `avg()`. Nantinya fungsi ini kita gunakan di view. -Setelah itu, siapkan fungsi `main()` dengan isi route handler untuk `/`. Di dalam handler ini, `view.html` diparsing, kemudian disisipkan fungsi yang telah dibuat di atas ke dalamnya. +Setelah itu, siapkan fungsi `main()` dengan isi route handler untuk `/`. Di dalam handler ini, `view.html` diparsing, kemudian fungsi yang telah dibuat di atas disisipkan ke dalam view. ```go func main() { @@ -86,7 +86,7 @@ Berikut merupakan penjelasan step-by-step mengenai kode panjang untuk parsing da 2. Fungsi custom yang telah kita buat, diregistrasikan agar dikenali oleh template tersebut. Bisa dilihat pada pemanggilan method `Funcs()`. 3. Setelah itu, lewat method `ParseFiles()`, view `view.html` di-parsing. Akan dicari dalam file tersebut apakah ada template yang didefinisikan dengan nama `view.html`. Karena di dalam template view tidak ada deklarasi template sama sekali (\{\{template "namatemplate"\}\}), maka akan dicari view yang namanya adalah `view.html`. Keseluruhan isi `view.html` akan dianggap sebagai sebuah template dengan nama template adalah nama file itu sendiri. -## B.8.3. Test +## B.8.3. Testing Tes hasilnya lewat browser. @@ -96,9 +96,9 @@ Tes hasilnya lewat browser. Pada kode di atas, pemanggilan `template.New()` menghasilkan objek bertipe `*template.Template`. -Pada chapter [B.5. Template: Render Partial HTML Template](/B-template-render-partial-html.html) kita telah belajar mengenai fungsi `template.ParseFiles()`, yang fungsi tersebut juga mengembalikan objek bertipe `*template.Template`. +Pada chapter [B.5. Template: Render Partial HTML Template](/B-template-render-partial-html.html) kita telah belajar mengenai fungsi `template.ParseFiles()` yang fungsi tersebut juga mengembalikan objek bertipe `*template.Template`. -Pada kode di atas, method `ParseFiles()` yang dipanggil bukanlah fungsi `template.ParseFiles()` yang kita telah pelajari sebelumnya. Meskipun namanya sama, kedua fungsi/method ini berbeda. +Di contoh di chapter ini, method `ParseFiles()` yang dipanggil bukanlah fungsi `template.ParseFiles()` yang kita telah pelajari sebelumnya. Meskipun namanya sama, kedua fungsi/method ini berbeda. - Fungsi `template.ParseFiles()`, adalah milik package `template`. Fungsi ini digunakan untuk mem-parsing semua view yang disisipkan sebagai parameter. - Method `ParseFiles()`, milik `*template.Template`, digunakan untuk memparsing semua view yang disisipkan sebagai parameter, lalu diambil hanya bagian yang nama template-nya adalah sama dengan nama template yang sudah di-alokasikan menggunakan `template.New()`. Jika template yang dicari tidak ada, maka akan mencari yang nama file-nya sama dengan nama template yang sudah ter-alokasi. @@ -108,7 +108,7 @@ Chapter selanjutnya akan membahas lebih detail mengenai penggunaan method `Parse --- diff --git a/content/B-template-functions.md b/content/B-template-functions.md index 86b59b70d..342a7b721 100644 --- a/content/B-template-functions.md +++ b/content/B-template-functions.md @@ -1,6 +1,8 @@ # B.7. Template: Functions -Go menyediakan beberapa *predefiend* function yang bisa digunakan dalam file template. Pada chapter ini kita akan membahas beberapa di antaranya beserta cara penggunaannya. Cara pemanggilan fungsi atau method sebuah objek pada file template sedikit berbeda dibanding seperti pada chapter sebelumnya. +Go menyediakan beberapa *predefiend* function yang bisa digunakan langsung dalam file template. Pada chapter ini kita akan membahas beberapa di antaranya beserta cara penggunaannya. + +Cara pemanggilan fungsi atau method sebuah objek pada file template sedikit berbeda dibanding dengan yang telah dicontohkan pada chapter sebelumnya. ## B.7.1. Persiapan @@ -160,7 +162,7 @@ Go juga menyediakan beberapa fungsi string yang bisa dimanfaatkan, yaitu: - `printf` (merupakan alias dari `fmt.Sprintf`) - `println` (merupakan alias dari `fmt.Sprintln`) -Cara penggunannya juga masih sama. +Cara penggunannya juga masih sama, contoh: ```html

@@ -231,7 +233,7 @@ Output program: ---

diff --git a/content/B-template-render-html.md b/content/B-template-render-html.md index ad1b757a2..31f3ec9d4 100644 --- a/content/B-template-render-html.md +++ b/content/B-template-render-html.md @@ -1,8 +1,8 @@ # B.4. Template: Render HTML Template -Pada bagian ini kita akan belajar bagaimana cara render file **template** ber-tipe **HTML**, untuk ditampilkan pada browser. +Pada bagian ini kita akan belajar bagaimana cara render file **template** yang berisi **HTML** untuk ditampilkan ke layar browser. -Terdapat banyak jenis template pada Go, yang akan kita pakai adalah template HTML. Package `html/template` berisi banyak sekali fungsi untuk kebutuhan rendering dan parsing file template jenis ini. +Terdapat banyak jenis template pada Go, di sini yang akan kita pakai adalah template HTML. Package `html/template` berisi banyak sekali fungsi untuk operasi rendering dan parsing file template HTML. ## B.4.1. Struktur Aplikasi @@ -57,15 +57,15 @@ if err != nil { Package `path` berisikan banyak fungsi yang berhubungan dengan lokasi folder atau path, yang salah satu di antaranya adalah fungsi `path.Join()`. Fungsi ini digunakan untuk menggabungkan folder atau file atau keduanya menjadi sebuah path, dengan separator relatif terhadap OS yang digunakan. -> Separator yang digunakan oleh `path.Join()` adalah `\` untuk wind\*ws dan `/` untuk un\*x. +> Separator yang digunakan oleh `path.Join()` adalah `\` untuk windows dan `/` untuk linux/unix/macos. Contoh penerapan `path.Join()` bisa dilihat di kode di atas, `views` di-join dengan `index.html`, menghasilkan `views/index.html`. Sedangkan `template.ParseFiles()`, digunakan untuk parsing file template, dalam contoh ini file `view/index.html`. Fungsi ini mengembalikan 2 data, yaitu hasil dari proses parsing yang bertipe `*template.Template`, dan informasi `error` jika ada. -Fungsi `http.Error()` digunakan untuk menandai response (`http.ResponseWriter`) bahwa terjadi error, dengan kode error dan pesan error bisa ditentukan. Pada contoh di atas yang digunakan adalah **500 - internal server error** yang direpresentasikan oleh variabel `http.StatusInternalServerError`. +Fungsi `http.Error()` digunakan untuk menandai HTTP request dengan response berupa error dengan kode serta pesan error bisa kita tentukan sendiri. Pada contoh di atas yang digunakan adalah **500 - internal server error**, direpresentasikan oleh variabel `http.StatusInternalServerError`. -Method `Execute()` milik `*template.Template`, digunakan untuk menyisipkan data pada template, untuk kemudian ditampilkan ke browser. Data bisa disipkan dalam bentuk `struct`, `map`, atau `interface{}`. +Method `Execute()` milik `*template.Template`, digunakan untuk menyisipkan data pada template, kemudian menampilkannya ke browser. Data bisa disipkan ke view dalam bentuk `struct`, `map`, atau `interface{}`. - Jika dituliskan dalam bentuk `map`, maka **key** akan menjadi nama variabel dan **value** menjadi nilainya - Jika dituliskan dalam bentuk variabel objek cetakan `struct`, nama **property** akan menjadi nama variabel @@ -94,13 +94,13 @@ Tanda titik "\." pada \{\{\.namaVariabel\}\} menerangkan bahwa variabel tersebut ## B.4.4. Testing -Semua sudah siap, maka jalankan program lalu lakukan testing via browser. +Semua sudah siap, sekarang jalankan program, lalu lakukan testing di browser. ![Output HTML](images/B_template_render_html_2_output.png) ## B.4.5. Static File CSS -Kita akan coba tambahkan sebuah stylesheet di sini. Langsung saja, buat file statis `assets/site.css`, isi dengan kode berikut. +Coba tambahkan sebuah stylesheet di sini. Buat file `assets/site.css`, isi dengan kode berikut. ```css body { @@ -139,7 +139,7 @@ Jalankan aplikasi untuk test hasil. --- diff --git a/content/B-template-render-partial-html.md b/content/B-template-render-partial-html.md index c86a4de30..160292781 100644 --- a/content/B-template-render-partial-html.md +++ b/content/B-template-render-partial-html.md @@ -1,15 +1,15 @@ # B.5. Template: Render Partial HTML Template -Satu buah halaman yang berisikan html, bisa terbentuk dari banyak template html (parsial). Pada chapter ini kita akan belajar bagaimana membuat, mem-parsing, dan me-render semua file tersebut. +Satu buah halaman yang berisikan html bisa saja terbentuk dari lebih dari satu proses parsing template html (parsial). Pada chapter ini kita akan belajar bagaimana membuat, mem-parsing, dan me-render semua template file. -Ada beberapa metode yang bisa digunakan, dari ke semuanya akan kita bahas 2 di antaranya, yaitu: +Ada beberapa metode yang bisa digunakan, 2 di antaranya: - Menggunakan fungsi `template.ParseGlob()`. - Menggunakan fungsi `template.ParseFiles()`. ## B.5.1. Struktur Aplikasi -Mari langsung kita praktekan. Buat project baru, siapkan file dan folder dengan susunan seperti dengan gambar berikut. +Mari belajar sambil praktek seperti biasa. Buat project baru, siapkan file dan folder dengan susunan seperti dengan gambar berikut. ![Structure](images/B_template_render_partial_html_1_structure.png) @@ -39,11 +39,11 @@ func main() { Tipe `M` merupakan alias dari `map[string]interface{}`, disiapkan untuk mempersingkat penulisan tipe map tersebut. Pada pembahasan-pembahasan selanjutnya kita akan banyak menggunakan tipe ini. -Pada kode di atas, di dalam fungsi `main()`, fungsi `template.ParseGlob()` dipanggil, dengan parameter adalah pattern path `"views/*"`. Fungsi ini digunakan untuk memparsing semua file yang match dengan pattern yang ditentukan, dan fungsi ini mengembalikan 2 objek: `*template.Template` & `error`. +Pada kode di atas, di dalam fungsi `main()`, fungsi `template.ParseGlob()` dipanggil, dengan parameter adalah pattern path `"views/*"`. Fungsi ini digunakan untuk memparsing semua file yang match dengan *pattern*/pola yang ditentukan. Fungsi ini mengembalikan 2 objek yaitu `*template.Template` & `error`. > Pattern path pada fungsi `template.ParseGlob()` nantinya akan di proses oleh `filepath.Glob()` -Proses parsing semua file html dalam folder `views` dilakukan di awal, agar ketika mengakses rute-tertentu-yang-menampilkan-html, tidak terjadi proses parsing lagi. +Proses parsing semua file html dalam folder `views` dilakukan di awal, agar ketika suatu endpoint diakses nantinya tidak terjadi proses parsing melainkan hanya proses rendering saja. > Parsing semua file menggunakan `template.ParseGlob()` yang dilakukan di luar handler, tidak direkomendasikan dalam fase development. Karena akan mempersulit testing html. Lebih detailnya akan dibahas di bagian bawah. @@ -164,7 +164,7 @@ Bisa dilihat pada gambar di atas, ketika rute `/index` dan `/about` di akses, ko ## B.5.6. Parsing Banyak File HTML Menggunakan `template.ParseFiles()` -Metode parsing menggunakan `template.ParseGlob()` memiliki kekurangan yaitu sangat tergantung terhadap pattern path yang digunakan. Jika dalam suatu proyek terdapat sangat banyak file html dan folder, sedangkan hanya beberapa yang digunakan, pemilihan pattern path yang kurang tepat akan menjadikan file lain ikut ter-parsing dengan sia-sia. +Metode parsing menggunakan `template.ParseGlob()` memiliki kekurangan yaitu sangat tergantung terhadap pattern path yang digunakan. Jika dalam suatu proyek terdapat sangat banyak file html dan folder, sedangkan hanya beberapa yang digunakan, pemilihan pattern path yang kurang tepat akan menjadikan file lain ikut ter-parsing sia-sia. Dan juga, karena statement `template.ParseGlob()` dieksekusi diluar handler, maka ketika ada perubahan pada salah satu view, lalu halaman di refresh, output yang dihasilkan akan tetap sama. Solusi dari masalah ini adalah dengan memanggil `template.ParseGlob()` di tiap handler rute-rute yang diregistrasikan. @@ -210,7 +210,7 @@ Mari kita praktekan. Ubah handler rute `/index` dan `/about`. Gunakan `template. }) ``` - - **Hapus** statement `template.ParseGlob()`. + - Tak lupa **hapus** statement `template.ParseGlob()`. ```go var tmpl, err = template.ParseGlob("views/*") @@ -229,7 +229,7 @@ Jalankan aplikasi untuk test hasilnya. --- diff --git a/content/C-advanced-configuration-viper.md b/content/C-advanced-configuration-viper.md index 8ce1fbd12..2d949af8f 100644 --- a/content/C-advanced-configuration-viper.md +++ b/content/C-advanced-configuration-viper.md @@ -150,7 +150,7 @@ Penggunaan fasilitas watcher memerlukan tambahan 3rd party library [fsnotify](ht --- diff --git a/content/C-advanced-middleware-and-logging.md b/content/C-advanced-middleware-and-logging.md index 529642b1f..3ec99b970 100644 --- a/content/C-advanced-middleware-and-logging.md +++ b/content/C-advanced-middleware-and-logging.md @@ -271,7 +271,7 @@ Satu kata, *cantik*. --- diff --git a/content/C-best-practice-configuration-env-var.md b/content/C-best-practice-configuration-env-var.md index 97aa1dd91..03f9a5188 100644 --- a/content/C-best-practice-configuration-env-var.md +++ b/content/C-best-practice-configuration-env-var.md @@ -157,7 +157,7 @@ Mungkin dari sini pembaca bisa lanjut ke chapter [C.35. Dockerize Aplikasi Golan --- diff --git a/content/C-client-http-request-advanced.md b/content/C-client-http-request-advanced.md index 78119776c..044f106d4 100644 --- a/content/C-client-http-request-advanced.md +++ b/content/C-client-http-request-advanced.md @@ -189,7 +189,7 @@ Jalankan aplikasi server, buka prompt terminal/CMD baru, lalu jalankan aplikasi --- diff --git a/content/C-convert-html-to-pdf.md b/content/C-convert-html-to-pdf.md index 590c3528e..ec4dce079 100644 --- a/content/C-convert-html-to-pdf.md +++ b/content/C-convert-html-to-pdf.md @@ -172,7 +172,7 @@ Cara ini cocok digunakan untuk konversi data HTML yang isinya muncul pada saat p --- diff --git a/content/C-cors-preflight-request.md b/content/C-cors-preflight-request.md index b1680b359..72415362c 100644 --- a/content/C-cors-preflight-request.md +++ b/content/C-cors-preflight-request.md @@ -292,7 +292,7 @@ Berikut adalah list konfigurasi yang bisa dimanfaatkan dari library ini. --- diff --git a/content/C-csrf.md b/content/C-csrf.md index 7938ef433..5bdd82fb0 100644 --- a/content/C-csrf.md +++ b/content/C-csrf.md @@ -220,7 +220,7 @@ Lewat teknik pencegahan ini, bukan berarti serangan CSRF tidak bisa dilakukan, s --- diff --git a/content/C-dockerize-golang.md b/content/C-dockerize-golang.md index b6b41990e..f75db6512 100644 --- a/content/C-dockerize-golang.md +++ b/content/C-dockerize-golang.md @@ -302,11 +302,11 @@ docker container run --name my-container-hello-world --rm -it -e PORT=8080 -e IN #### ◉ Flag `--rm` -Flag ini digunakan untuk meng-automatisasi proses penghapusan container sewaktu container tersebut di stop. Jadi kita tidak perlu delete manual pakai `docker container rm`. Hal ini sangat membantu karena *command* `docker run` akan membuat container baru setiap dijalankan. Tapi sebenarnya pada contoh sebelumnya kita tidak perlu khawatir akan dibuat container baru karena sudah ada flag `--name`. Flag tersebut digunakan untuk menentukan nama container, yang di mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau pembaca tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`. +Flag ini digunakan untuk meng-automatisasi proses penghapusan container sewaktu container tersebut di stop. Jadi kita tidak perlu delete manual pakai `docker container rm`. Hal ini sangat membantu karena *command* `docker run` akan membuat container baru setiap dijalankan. Tapi sebenarnya pada contoh sebelumnya kita tidak perlu khawatir akan dibuat container baru karena sudah ada flag `--name`. Flag tersebut digunakan untuk menentukan nama container, yang mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau pembaca tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`. #### ◉ Flag `-it` -Flag ini merupakan flag gabungan antara `-i` yang digunakan untuk meng-enable *interactive mode* dan `-t` untuk *enable* `TTY`. Dengan ini kita bisa masuk ke mode interaktif yang di mana jika kita terminate atau kill command menggunakan `CTRL + C` atau `CMD + C` (untuk mac), maka otomatis container akan di stop. +Flag ini merupakan flag gabungan antara `-i` yang digunakan untuk meng-enable *interactive mode* dan `-t` untuk *enable* `TTY`. Dengan ini kita bisa masuk ke mode interaktif yang mana jika kita terminate atau kill command menggunakan `CTRL + C` atau `CMD + C` (untuk mac), maka otomatis container akan di stop. Nah dengan menggabungkan flag `--rm` dan flag `-it` kita bisa dengan mudah stop kemudian hapus container. @@ -320,7 +320,7 @@ docker container stop my-container-hello-world --- diff --git a/content/C-echo-routing.md b/content/C-echo-routing.md index 9ea7e7a1d..8c7d3217f 100644 --- a/content/C-echo-routing.md +++ b/content/C-echo-routing.md @@ -279,7 +279,7 @@ Jalankan aplikasi, lalu coba akses `http://localhost:9000/static/layout.js`. --- diff --git a/content/C-echo-template-rendering.md b/content/C-echo-template-rendering.md index 5868aec4f..2603b1815 100644 --- a/content/C-echo-template-rendering.md +++ b/content/C-echo-template-rendering.md @@ -4,7 +4,7 @@ Pada chapter ini kita akan belajar cara render template html pada aplikasi yang Pada dasarnya proses parsing dan rendering template tidak di-handle oleh echo sendiri, melainkan oleh API dari package `html/template`. Jadi bisa dibilang cara render template di echo adalah sama seperti pada aplikasi yang murni menggunakan golang biasa, seperti yang sudah dibahas pada chapter [Template: Render HTML Template](/B-template-render-html.html), [Template: Render Partial HTML Template](/B-template-render-partial-html.html), [Template: Render Specific HTML Template](/B-render-specific-html.html), dan [Template: Render HTML String](/B-render-html-string.html). -Echo menyediakan satu fasilitas yang bisa kita manfaatkan untuk standarisasi rendering template. Cara penggunaannya, dengan meng-override default `.Renderer` property milik echo menggunakan objek cetakan struct, yang di mana pada struct tersebut harus ada method bernama `.Render()` dengan skema sesuai dengan kebutuhan echo. Nah, di dalam method `.Render()` inilah kode untuk parsing dan rendering template ditulis. +Echo menyediakan satu fasilitas yang bisa kita manfaatkan untuk standarisasi rendering template. Cara penggunaannya, dengan meng-override default `.Renderer` property milik echo menggunakan objek cetakan struct, yang mana pada struct tersebut harus ada method bernama `.Render()` dengan skema sesuai dengan kebutuhan echo. Nah, di dalam method `.Render()` inilah kode untuk parsing dan rendering template ditulis. ## C.7.1. Praktek @@ -137,7 +137,7 @@ Proses parsing dan rendering tidak di-handle oleh echo, melainkan menggunakan AP --- diff --git a/content/C-flag-parser.md b/content/C-flag-parser.md index bf3ef968e..b547daec0 100644 --- a/content/C-flag-parser.md +++ b/content/C-flag-parser.md @@ -336,7 +336,7 @@ Cobra merupakan library yang dirancang khusus untuk development aplikasi berbasi --- diff --git a/content/C-golang-aws-s3.md b/content/C-golang-aws-s3.md index b731b568c..00da51527 100644 --- a/content/C-golang-aws-s3.md +++ b/content/C-golang-aws-s3.md @@ -437,7 +437,7 @@ Hasilnya: --- diff --git a/content/C-golang-ftp.md b/content/C-golang-ftp.md index db0a484eb..0f0fc05a4 100644 --- a/content/C-golang-ftp.md +++ b/content/C-golang-ftp.md @@ -293,7 +293,7 @@ Jalankan aplikasi, cek hasilnya. Untuk memvalidasi bahwa file di client dan di s --- diff --git a/content/C-golang-grpc-protobuf.md b/content/C-golang-grpc-protobuf.md index f202307db..6b2e0336c 100644 --- a/content/C-golang-grpc-protobuf.md +++ b/content/C-golang-grpc-protobuf.md @@ -541,7 +541,7 @@ OK, jika anda membaca sampai baris ini, berarti anda telah berhasil sabar dalam --- diff --git a/content/C-golang-jwt.md b/content/C-golang-jwt.md index 8e73621c4..8bc111aff 100644 --- a/content/C-golang-jwt.md +++ b/content/C-golang-jwt.md @@ -407,7 +407,7 @@ Semua berjalan sesuai harapan. Agar lebih meyakinkan, coba lakukan beberapa test --- diff --git a/content/C-golang-ldap-authentication.md b/content/C-golang-ldap-authentication.md index 17fdb17a3..2b454fe3e 100644 --- a/content/C-golang-ldap-authentication.md +++ b/content/C-golang-ldap-authentication.md @@ -317,7 +317,7 @@ if err != nil { --- diff --git a/content/C-golang-protobuf-implementation.md b/content/C-golang-protobuf-implementation.md index 9f363cd4f..c76cb12a8 100644 --- a/content/C-golang-protobuf-implementation.md +++ b/content/C-golang-protobuf-implementation.md @@ -390,7 +390,7 @@ Pada chapter selanjutnya kita akan belajar tentang penerapan gRPC dan protobuf. --- diff --git a/content/C-golang-redis.md b/content/C-golang-redis.md index 1d0e01715..76d0a2a7e 100644 --- a/content/C-golang-redis.md +++ b/content/C-golang-redis.md @@ -163,7 +163,7 @@ Error, ini karena data yang disimpan hanya di retain sesuai `ttl` yaitu 3 detik, --- diff --git a/content/C-golang-ssh-sftp.md b/content/C-golang-ssh-sftp.md index a816f87fc..bdab2d295 100644 --- a/content/C-golang-ssh-sftp.md +++ b/content/C-golang-ssh-sftp.md @@ -264,7 +264,7 @@ Jalankan aplikasi untuk melihat hasilnya. --- diff --git a/content/C-golang-sso-saml-sp.md b/content/C-golang-sso-saml-sp.md index 45db97f02..583afbd6b 100644 --- a/content/C-golang-sso-saml-sp.md +++ b/content/C-golang-sso-saml-sp.md @@ -272,7 +272,7 @@ http.ListenAndServe(portString, nil) go run *.go ``` -Oops, muncul error pada saat mengakses `http://localhost:9000/index`. Meski url ini merupakan protected url, yang di mana hanya bisa diakses ketika sudah login, harusnya user akan di-redirect ke halaman login, bukan malah memunculkan error. +Oops, muncul error pada saat mengakses `http://localhost:9000/index`. Meski url ini merupakan protected url, yang mana hanya bisa diakses ketika sudah login, harusnya user akan di-redirect ke halaman login, bukan malah memunculkan error. ![Metadata share](images/C_golang_sso_saml_sp_1_saml_metadata_missing.png) @@ -311,7 +311,7 @@ Salah satu benefit metode inisialisasi ini: ketika ada banyak aplikasi SP (misal --- diff --git a/content/C-http-error-handling.md b/content/C-http-error-handling.md index c45a2cba6..4fed93576 100644 --- a/content/C-http-error-handling.md +++ b/content/C-http-error-handling.md @@ -105,7 +105,7 @@ Silakan ubah kode `fmt.Sprintf("%d.html", report.Code)` sesuai format nama halam --- diff --git a/content/C-http-gzip-compression.md b/content/C-http-gzip-compression.md index 70cb27dab..bfacf39fb 100644 --- a/content/C-http-gzip-compression.md +++ b/content/C-http-gzip-compression.md @@ -141,7 +141,7 @@ e.Logger.Fatal(e.Start(":9000")) --- diff --git a/content/C-http-request-payload-validation.md b/content/C-http-request-payload-validation.md index e05577959..52405d187 100644 --- a/content/C-http-request-payload-validation.md +++ b/content/C-http-request-payload-validation.md @@ -109,7 +109,7 @@ Nah, pada chapter selanjutnya kita akan belajar cara membuat custom error handle --- diff --git a/content/C-http2-server-push.md b/content/C-http2-server-push.md index 042489085..723845978 100644 --- a/content/C-http2-server-push.md +++ b/content/C-http2-server-push.md @@ -162,7 +162,7 @@ Selain dari kolom protocol, penanda server push bisa dilihat juga lewat grafik * --- diff --git a/content/C-https-tls.md b/content/C-https-tls.md index 70e63931f..e8726619a 100644 --- a/content/C-https-tls.md +++ b/content/C-https-tls.md @@ -126,12 +126,12 @@ Coba test juga menggunakan browser, jika menggunakan chrome maka akan muncul war ![browser example](images/C_https_tls_5_browser_example.png) -Warning `NET::ERR_CERT_AUTHORITY_INVALID` muncul ketika mengakses sebuah website menggunakan protokol `https` yang di mana website ini mengaplikasikan **self-signed certificate**, bukan menggunakan certificate yang sudah diverifikasi oleh CA. +Warning `NET::ERR_CERT_AUTHORITY_INVALID` muncul ketika mengakses sebuah website menggunakan protokol `https` yang mana website ini mengaplikasikan **self-signed certificate**, bukan menggunakan certificate yang sudah diverifikasi oleh CA. --- diff --git a/content/C-parsing-http-request-payload-echo.md b/content/C-parsing-http-request-payload-echo.md index c55917c5f..896068830 100644 --- a/content/C-parsing-http-request-payload-echo.md +++ b/content/C-parsing-http-request-payload-echo.md @@ -115,7 +115,7 @@ curl -X GET http://localhost:9000/user?name=Joe&email=nope@novalagung.com --- diff --git a/content/C-read-write-excel-xlsx-file.md b/content/C-read-write-excel-xlsx-file.md index 7bd3311a0..5e729154e 100644 --- a/content/C-read-write-excel-xlsx-file.md +++ b/content/C-read-write-excel-xlsx-file.md @@ -192,7 +192,7 @@ Jalankan aplikasi untuk mengecek hasilnya. --- diff --git a/content/C-scraping-parsing-html.md b/content/C-scraping-parsing-html.md index ec7def3b3..822764d79 100644 --- a/content/C-scraping-parsing-html.md +++ b/content/C-scraping-parsing-html.md @@ -190,7 +190,7 @@ Jalankan aplikasi, lihat hasilnya. --- diff --git a/content/C-secure-insecure-client-http-request.md b/content/C-secure-insecure-client-http-request.md index 8eb6646d8..f180245b1 100644 --- a/content/C-secure-insecure-client-http-request.md +++ b/content/C-secure-insecure-client-http-request.md @@ -185,7 +185,7 @@ Tujuan mengapa penulis tambahkan sub chapter **Konfigurasi SSL/TLS Lanjutan** in --- diff --git a/content/C-secure-middleware.md b/content/C-secure-middleware.md index df4469d40..60ee73267 100644 --- a/content/C-secure-middleware.md +++ b/content/C-secure-middleware.md @@ -149,7 +149,7 @@ Lebih mendetailnya silakan langsung cek halaman official library secure di https --- diff --git a/content/C-securecookie.md b/content/C-securecookie.md index ea3fe2779..319e504c2 100644 --- a/content/C-securecookie.md +++ b/content/C-securecookie.md @@ -153,7 +153,7 @@ http.SetCookie(c.Response(), cookie) --- diff --git a/content/C-send-email.md b/content/C-send-email.md index 14025d460..5d8fe368f 100644 --- a/content/C-send-email.md +++ b/content/C-send-email.md @@ -194,7 +194,7 @@ Daaaaannnnn ... cukup itu saja penyesuaiannya agar bisa kirim email via konfigur --- diff --git a/content/C-session.md b/content/C-session.md index d3963c8f3..069fbc1de 100644 --- a/content/C-session.md +++ b/content/C-session.md @@ -310,7 +310,7 @@ Tujuan dari kode yang kita tulis kurang lebih sebagai berikut. --- diff --git a/content/C-singleflight.md b/content/C-singleflight.md index 33a7c549b..67e41441e 100644 --- a/content/C-singleflight.md +++ b/content/C-singleflight.md @@ -235,7 +235,7 @@ Cukup berguna bukan? Dengan adahnya singleflight API ini, beban backend akan sed --- diff --git a/content/C-write-pdf-file.md b/content/C-write-pdf-file.md index ce7d0529f..043ba38e2 100644 --- a/content/C-write-pdf-file.md +++ b/content/C-write-pdf-file.md @@ -87,7 +87,7 @@ Coba jalankan aplikasi untuk melihat hasilnya. Buka generated file `file.pdf`, i --- diff --git a/content/C-xml-parser.md b/content/C-xml-parser.md index d8f7a2120..ad948e2f7 100644 --- a/content/C-xml-parser.md +++ b/content/C-xml-parser.md @@ -274,7 +274,7 @@ Jalankan aplikasi, lihat hasilnya. --- diff --git a/content/D-golang-web-socket-chatting-app.md b/content/D-golang-web-socket-chatting-app.md index 50286d74d..3b543c6ce 100644 --- a/content/D-golang-web-socket-chatting-app.md +++ b/content/D-golang-web-socket-chatting-app.md @@ -414,7 +414,7 @@ Ketika salah satu user leave, pesan **User XXX: disconnected** akan di-broadcast --- diff --git a/content/D-google-api-search.md b/content/D-google-api-search.md index 30d62ed6c..f519f45ff 100644 --- a/content/D-google-api-search.md +++ b/content/D-google-api-search.md @@ -325,7 +325,7 @@ Error di atas muncul karena, host `localhost` belum didaftarkan pada API console --- diff --git a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md index b2c46a01c..cb51c12e2 100644 --- a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md +++ b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md @@ -16,7 +16,7 @@ Dengan metode worker pool ini, maka penggunaan memory dan performansi program ak *Connection pool* adalah metode untuk manajemen sejumlah koneksi database, agar bisa digunakan secara optimal. -Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan database yang di mana concurrent programming diterapkan. +Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan database yang mana concurrent programming diterapkan. Karena pada concurrent programming, beberapa proses akan berjalan bersamaan, maka penggunaan 1 koneksi db akan menghambat proses tersebut. Perlu ada beberapa koneksi database, agar goroutine tidak rebutan objek koneksi database. @@ -342,7 +342,7 @@ Praktek pada chapter ini sifatnya adalah POC, jadi sangat mungkin diperlukan pen --- diff --git a/content/README.md b/content/README.md index 3d385ec2a..e0f28b35a 100644 --- a/content/README.md +++ b/content/README.md @@ -35,7 +35,7 @@ Ebook ini bisa di-download dalam bentuk file via link berikut: Untuk mendapatkan konten buku yang paling update, silakan baca langsung versi web secara online atau download ulang e-book versi terbaru. -## Source Code Praktek +## Source Code Praktik Source code contoh-contoh program bisa diunduh di [github.com/novalagung/dasarpemrogramangolang-example](https://github.com/novalagung/dasarpemrogramangolang-example). Dianjurkan untuk tidak copy-paste dari source code dalam proses belajar, usahakan untuk menulis sendiri kode program agar cepat terbiasa dengan bahasa Go.