Pengembangan server REST di Go. Bagian 2: menerapkan router gorila / mux

Ini adalah artikel kedua dari rangkaian artikel tentang pengembangan server REST di Go. Pada artikel pertama dalam seri ini, kami membuat server sederhana menggunakan alat Go standar, lalu memfaktorkan ulang kode pembuatan data JSON, menjadikannya fungsi pembantu. Ini memungkinkan kami untuk sampai pada kode yang cukup ringkas untuk penangan rute.



Di sana kami berbicara tentang satu masalah dengan server kami, yaitu bahwa logika perutean tersebar di beberapa tempat dalam program kami.







Ini adalah masalah yang dihadapi setiap orang yang menulis server HTTP tanpa menggunakan dependensi. Kecuali jika server, dengan mempertimbangkan sistem rutenya, bukanlah desain yang sangat minimalis (misalnya, ini adalah beberapa server khusus yang hanya memiliki satu atau dua rute), maka ternyata ukuran dan kompleksitas organisasi kode router adalah sesuatu yang diperhatikan oleh programmer berpengalaman dengan sangat cepat.



Sistem perutean yang ditingkatkan



Pikiran pertama yang mungkin muncul pada seseorang yang memutuskan untuk meningkatkan server kami mungkin adalah ide untuk mengabstraksi sistem peruteannya, mungkin menggunakan seperangkat fungsi atau tipe data dengan metode. Ada banyak pendekatan menarik untuk memecahkan masalah ini, yang dapat diterapkan dalam setiap situasi tertentu. Ekosistem Go memiliki banyak perpustakaan pihak ketiga yang kuat yang telah berhasil digunakan di berbagai proyek untuk mengimplementasikan kemampuan router. Saya sangat menyarankan untuk melihat materi ini , yang membandingkan beberapa pendekatan untuk menangani rangkaian rute sederhana.



Sebelum beralih ke contoh praktis, mari kita ingat bagaimana API server kita bekerja:



POST   /task/              :       ID
GET    /task/<taskid>      :       ID
GET    /task/              :    
DELETE /task/<taskid>      :     ID
GET    /tag/<tagname>      :       
GET    /due/<yy>/<mm>/<dd> :    ,    

      
      





Untuk membuat sistem perutean lebih nyaman, kita dapat melakukan ini:



  1. Anda dapat membuat mekanisme yang memungkinkan Anda untuk menentukan penangan terpisah untuk metode berbeda dari rute yang sama. Misalnya, permintaan POST /task/



    harus diproses oleh satu penangan, dan permintaan GET /task/



    oleh yang lain.
  2. Anda dapat membuatnya sehingga pengendali rute dipilih berdasarkan analisis permintaan yang lebih dalam daripada sekarang. Misalnya, dengan pendekatan ini, kita harus dapat menunjukkan bahwa satu handler memproses permintaan ke /task/



    , dan handler lain memproses permintaan /task/<taskid>



    dengan numerik ID



    .
  3. Dalam hal ini, sistem pemrosesan rute harus mengekstrak numerik ID



    dari /task/<taskid>



    dan meneruskannya ke handler dengan cara yang nyaman bagi kita.


Menulis router Anda sendiri di Go sangat mudah. Ini karena Anda dapat mengatur pekerjaan Anda dengan penangan HTTP menggunakan tata letak. Tapi di sini saya tidak akan menuruti keinginan saya untuk menulis semuanya sendiri. Sebagai gantinya, saya mengusulkan untuk berbicara tentang cara mengatur sistem perutean menggunakan salah satu router paling populer yang disebut gorila / mux .



Server aplikasi manajemen tugas menggunakan gorila / mux



Paket gorila / mux adalah salah satu router HTTP tertua dan terpopuler untuk Go. Kata "mux", sesuai dengan dokumentasi paket , adalah singkatan dari "HTTP request multiplexer" ("mux" memiliki arti yang sama di perpustakaan standar).



Karena ini adalah paket yang ditujukan untuk menyelesaikan satu tugas yang sangat khusus, sangat mudah untuk menggunakannya. Varian dari server kami yang menggunakan gorila/mux untuk routing dapat ditemukan di sini . Berikut adalah kode untuk mendefinisikan rute:



router := mux.NewRouter()
router.StrictSlash(true)
server := NewTaskServer()

router.HandleFunc("/task/", server.createTaskHandler).Methods("POST")
router.HandleFunc("/task/", server.getAllTasksHandler).Methods("GET")
router.HandleFunc("/task/", server.deleteAllTasksHandler).Methods("DELETE")
router.HandleFunc("/task/{id:[0-9]+}/", server.getTaskHandler).Methods("GET")
router.HandleFunc("/task/{id:[0-9]+}/", server.deleteTaskHandler).Methods("DELETE")
router.HandleFunc("/tag/{tag}/", server.tagHandler).Methods("GET")
router.HandleFunc("/due/{year:[0-9]+}/{month:[0-9]+}/{day:[0-9]+}/", server.dueHandler).Methods("GET")

      
      





Harap dicatat bahwa definisi ini saja segera menutup dua item pertama dari daftar tugas di atas yang perlu diselesaikan untuk meningkatkan kenyamanan bekerja dengan rute. Karena kenyataan bahwa panggilan digunakan dalam deskripsi Methods



route , kita dapat dengan mudah menetapkan metode yang berbeda untuk penangan yang berbeda dalam satu rute. Pencocokan template (menggunakan ekspresi reguler) dengan cara memungkinkan kita untuk dengan mudah membedakan /task/



dan /task/<taskid>



pada deskripsi rute tingkat atas.



Untuk menangani tugas, yang ada di paragraf ketiga daftar kami, mari kita lihat penggunaannya getTaskHandler



:



func (ts *taskServer) getTaskHandler(w http.ResponseWriter, req *http.Request) {
  log.Printf("handling get task at %s\n", req.URL.Path)

  //          Atoi,   
  //   ,    [0-9]+.
  id, _ := strconv.Atoi(mux.Vars(req)["id"])
  ts.Lock()
  task, err := ts.store.GetTask(id)
  ts.Unlock()

  if err != nil {
    http.Error(w, err.Error(), http.StatusNotFound)
    return
  }

  renderJSON(w, task)
}

      
      





Dalam definisi rute, rute /task/{id:[0-9]+}/



menjelaskan ekspresi reguler yang digunakan untuk mengurai jalur dan menetapkan pengidentifikasi ke "variabel" id



. "Variabel" ini dapat diakses dengan memanggil fungsi mux.Vars



dan meneruskannya req



(gorila / mux menyimpan variabel ini dalam konteks setiap permintaan, dan mux.Vars



merupakan fungsi pembantu yang nyaman untuk bekerja dengannya).



Perbandingan pendekatan yang berbeda untuk mengatur perutean



Inilah tampilan urutan pembacaan kode dalam versi server asli bagi mereka yang ingin memahami bagaimana suatu rute diproses GET /task/<taskid>



.





Inilah yang harus dibaca jika Anda ingin memahami kode yang menggunakan gorila / mux:





Saat menggunakan gorila / mux, Anda tidak hanya harus "melompat" lebih sedikit melalui teks program. Di sini, sebagai tambahan, Anda harus membaca lebih sedikit kode. Menurut pendapat saya, ini sangat bagus dalam hal meningkatkan keterbacaan kode. Menggambarkan jalur saat menggunakan gorila / mux adalah tugas sederhana dan hanya membutuhkan sedikit kode untuk dipecahkan. Dan siapapun yang membaca kode ini akan langsung mengerti bagaimana kode ini bekerja. Keuntungan lain dari pendekatan ini adalah bahwa semua rute dapat dilihat secara harfiah dengan melihat kode di satu tempat. Dan, pada kenyataannya, kode pengaturan perutean sekarang terlihat sangat mirip dengan deskripsi bentuk bebas dari REST API kami.



Saya suka menggunakan paket seperti gorila / mux karena mereka adalah alat yang sangat khusus. Mereka memecahkan satu masalah tunggal dan mereka melakukannya dengan baik. Mereka tidak "merangkak" ke setiap sudut kode program proyek, yang berarti bahwa, jika perlu, mereka dapat dengan mudah dihapus atau diganti dengan yang lain. Jika Anda melihat kode lengkapnyadari varian server yang sedang kita bicarakan dalam artikel ini, Anda dapat melihat bahwa ruang lingkup mekanisme gorila / mux terbatas pada beberapa baris kode. Jika, seiring perkembangan proyek, beberapa batasan ditemukan dalam paket gorila / mux yang tidak sesuai dengan spesifikasi proyek ini, tugas mengganti gorila / mux dengan router pihak ketiga lain (atau dengan router Anda sendiri) harus diselesaikan dengan cepat dan mudah.



Router apa yang akan Anda gunakan saat mengembangkan server REST di Go?








All Articles