DEV Community

Phondanai
Phondanai

Posted on • Edited on

2 2

Clojure Ring เบื้องต้น

ได้เขียนไว้ที่นี่อีกที่นึง

วันนี้จะมาลองอธิบายการสร้างเว็บด้วย Clojure โดยใช้ library ที่ชื่อว่า ring กัน
โดยตัวอย่างที่ยกมา เอามาจากหน้า wiki ของ ring ใน github

เกี่ยวกับ Ring

ring คือ

  • Library ที่ใช้กันแพร่หลาย สำหรับพัฒนาเว็บโดยใช้ภาษา Clojure
  • สามารถสร้างเว็บและคอมไพล์ให้เป็น Java servlet ได้
  • สร้าง package Java war เพื่อเอาไป deploy ต่อได้ง่าย

สิ่งที่ต้องมี

hello ring

ต่อไปเราจะเริ่มสร้างเว็บแบบง่ายๆ กัน

  • สร้าง project ใหม่ด้วย Leiningen:
$ lein new hello-world
$ cd hello-world
Enter fullscreen mode Exit fullscreen mode
  • เพิ่ม ring-core และ ring-jetty-adapter ลงใน dependencies ในไฟล์ project.clj
(defproject hello-world "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [ring/ring-core "1.8.2"]
                 [ring/ring-jetty-adapter "1.8.2"]]
  :repl-options {:init-ns hello-world.core})
Enter fullscreen mode Exit fullscreen mode

ดาวน์โหลด dependencies:

$ lein deps
Enter fullscreen mode Exit fullscreen mode

ในไฟล์ src/hello-world/core.clj ให้สร้าง handler อย่างง่ายขึ้นมา (handler ก็คือฟังก์ชัน)

(ns hello-world.core)

(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "Hello World"})
Enter fullscreen mode Exit fullscreen mode

จากนั้นให้ start REPL ขึ้นมาโดยใช้ Leiningen

$ lein repl
Enter fullscreen mode Exit fullscreen mode

หลังจาก REPL รันขึ้นมาแล้ว, รัน Jetty ขึ้นมาเพื่อใช้ handler ที่เราสร้างขึ้น (Jetty คือ web server ตัวนึง)

=> (use 'ring.adapter.jetty)
=> (use 'hello-world.core)
=> (run-jetty handler {:port 3000
                       :join? false)
Enter fullscreen mode Exit fullscreen mode

web server จะถูกรันขึ้นมา โดยตอนนี้สามารถเปิดเว็บเบราว์เซอร์ขึ้นมาแล้วเปิดหน้าเว็บ http://localhost:3000/ เพื่อดูผลลัพธ์

result

เอาล่ะ ตอนนี้เราก็ได้เว็บง่ายๆ โง่ๆ มาอันนึงที่ response กลับมาทุกครั้งว่า Hello World
เรามาดู concept ที่ใช้ใน ring กัน ว่ามีไรบ้าง

Ring concepts

ใน ring จะมี concepts หลักๆ อยู่ 4 อย่าง

  • Handler
  • Request
  • Response
  • Middleware

Handlers

Handlers ก็คือฟังก์ชันนี่แหละ ที่คอยรับ HTTP request --> process request --> ส่ง response กลับมา
, โดย request กับ response ใน handler เนี่ย จะอยู่ในรูปแบบ Clojure map และ ring จะจัดการที่เหลือให้ (เช่น แปลงเป็น HTTP reponse string)

ตัวอย่าง handler ที่โชว์ ip address ของ client ที่ส่ง request เข้ามา

(defn what-is-my-ip [request]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body (:remote-addr request)})
Enter fullscreen mode Exit fullscreen mode

remote address

จะเห็นได้ว่า ตัวอย่างนั้นใช้การดึงค่าออกมาจาก request map โดยใช้คีย์ :remote-addr

Requests

HTTP request จะอยู่ในรูปแบบ Clojure map ซึ่งก็จะมี keys มาตรฐานติดมา และก็ยังสามารถมี key ที่ custom เพิ่มเองได้
เช่น สร้างมาจาก middleware

key มาตรฐาน

  • :server-port - พอร์ตของ server ที่ request เรียกเข้ามา
  • :server-name - ชื่อของ server ที่ request เรียกเข้ามา
  • :remote-addr - Ip address ของ client request ที่เรียกเข้ามา <-- ที่ใช้ในตัวอย่างข้างบน
  • :uri - URI หรือ path ที่ request เรียกเข้ามา
  • :query-string - ตรงตัวเลยคือ query-string
  • :scheme protocol - ที่ใช้ request เช่น :http หรือ :https
  • :request-method - request method ที่เรียกมา เช่น :get, :head, :options, :put, :post หรือ :delete
  • :headers - Clojure map ของชื่อ header ต่างๆ
  • :body - InputStream จาก request body

Responses

Response map นั้น จะถูกสร้างขึ้นมาโดย handler ซึ่งมีทั้งหมด 3 key หลัก:

  • :status HTTP status code เช่น 200, 302, 404
  • :headers Clojure map ของ ชื่อ HTTP header
  • :body ตรงนี้จะเป็น response ที่จะตอบกลับไปยัง request, body ใน ring จะรองรับข้อมูลอยู่ 4 ประเภท คือ
    • string ส่ง string ตรงๆ ไปยัง client
    • ISeq แต่ละสมาชิกใน seq จะถูกส่งไปเป็น string
    • File เนื้อหาในไฟล์จะถูกส่งไปยัง client
    • InputStream เนื้อหาใน stream จะถูกส่งไปยัง client จนกว่า stream จะปิด

Middleware

middleware คือส่วนที่ทำให้เพิ่มความสามารถหรือทำอะไรบางอย่างเพิ่มเติมกับ handler ได้ (ในเอกสารเรียกว่าเป็น higher-level function)
middleware นั้น รับ handler เป็น input และ output ก็เป็น handler เช่นกัน
แต่ข้างในจะมีการเพิ่มเติมข้อมูลลงไป เช่น เปลี่ยน header หรืออะไรก็แล้วแต่

ตัวอย่าง middleware

(defn wrap-content-type [handler content-type]
  (fn [request]
    (let [response (handler request)]
      (assoc-in response [:headers "Content-Type"] content-type))))
Enter fullscreen mode Exit fullscreen mode

จะเห็นว่า middleware นั้น return ออกมาเป็นฟังก์ชัน หรือ handler นี่แหละ
ข้างในจะมีการเปลี่ยน header "Content-Type" ให้เป็นตามตัวแปร content-type

ตัวอย่างการใช้ middleware

(def app
  (wrap-content-type handler "text/html"))
Enter fullscreen mode Exit fullscreen mode

จากตัวอย่างแปลว่า มีการเปลี่ยน header "Content-Type" ให้กลายเป็นประเภท "text/html"

โดยปกติแล้ว เราสามารถซ้อน middleware ต่อกันไปได้เรื่อยๆ (เค้าเรียกว่า chain อ่ะนะ) กี่ชั้นก็ว่าไป
Clojure ก็จะมี threading macro (->) syntax เพื่อช่วยในการ chain middleware ต่างๆ เช่น

(def app
  (-> handler
      (wrap-content-type "text/html")
      (wrap-keyword-params)
      (wrap-params)))
Enter fullscreen mode Exit fullscreen mode

middleware นั้นถูกใช้เป็นเรื่องปกติใน Ring, ไม่ว่าจะช่วยในเรื่องการจัดการ parameters, sessions หรือ อัพโหลดไฟล์
ทั้งหมดทั้งมวลนี้ถูกจัดการโดยใช้ middleware ของ Ring

เอาล่ะคงเท่านี้ก่อน เพราะน่าจะยาวแล้ว

The Fastest, Most Accurate API for Voice AI

Ad Image

Building an AI Agent that needs to deliver human-like conversations? | Speechmatics’ real-time ASR is available in 50 languages and can understand speech in a fraction of a second.

Try Free

Top comments (0)

Sentry workshop image

Flaky tests got you down?

Learn how to merge your code without having to hit “rerun” every 5 minutes 😮‍💨

Save your spot now.

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay