Skip to content

Commit

Permalink
feat: improve content
Browse files Browse the repository at this point in the history
  • Loading branch information
novalagung committed Apr 27, 2024
1 parent 0b5fcfe commit 7f495eb
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 103 deletions.
35 changes: 19 additions & 16 deletions content/B-ajax-json-payload.md
Original file line number Diff line number Diff line change
@@ -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 `<form />`. 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 `<form />`
[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 `<form />`, 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 `<form />` 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 `<form />` 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 `<form />` 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

Expand All @@ -43,7 +45,7 @@ Layout dari view perlu disiapkan terlebih dahulu, tulis kode berikut pada file `
</html>
```

Selanjutnya, pada tag `<form />` 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 `<form />` 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
<table noborder>
Expand Down Expand Up @@ -79,7 +81,7 @@ Selanjutnya, pada tag `<form />` 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 `<form />` 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 `<form />` di-override, diganti dengan AJAX request.

```js
$("#user-form").on("submit", function (e) {
Expand All @@ -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 `<p class="message"></p>`.
Respon dari AJAX di atas nantinya dimunculkan pada `<p class="message"></p>`.

## B.14.4. Back End

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -175,17 +177,18 @@ 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

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)

Expand Down
20 changes: 9 additions & 11 deletions content/B-ajax-json-response.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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) {
Expand All @@ -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")
Expand All @@ -80,7 +78,7 @@ 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()`.

---

Expand Down
44 changes: 22 additions & 22 deletions content/B-ajax-multi-upload.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# 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)

> Silakan unduh file js jQuery dari situs official jQuery.
## 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
<!DOCTYPE html>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand All @@ -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 {
Expand Down Expand Up @@ -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

Expand Down
Loading

0 comments on commit 7f495eb

Please sign in to comment.