Giới thiệu về thư viện Req
Req là một thư viện HTTP client cấp cao trong Haskell, nổi bật với tính an toàn về kiểu dữ liệu, khả năng mở rộng và dễ sử dụng. Thư viện này tích hợp tốt với hệ thống kiểu của Haskell và hỗ trợ xử lý các request HTTP một cách rõ ràng, mạnh mẽ.
Cài đặt thư viện qua Cabal:
cabal install req
Sau khi cài đặt, bạn có thể bắt đầu sử dụng trong môi trường GHCi:
:m +Network.HTTP.Req
Ví dụ cơ bản với Req
Dưới đây là một số ví dụ minh họa cách gửi các loại request phổ biến như GET, POST, PUT và DELETE đến các API công cộng như httpbin.org và jsonplaceholder.typicode.com.
GET Request – Lấy dữ liệu dạng thô
Gửi yêu cầu GET để nhận dữ liệu nhị phân từ httpbin.org/bytes:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Req
import qualified Data.ByteString.Char8 as BS
fetchBytes :: IO ()
fetchBytes = runReq defaultHttpConfig $ do
response <- req GET (https "httpbin.org" /: "bytes" /~ (5 :: Int))
NoReqBody
bsResponse
mempty
liftIO $ BS.putStrLn (responseBody response)
GET với tham số truy vấn và xác thực
Khi URL hoặc các tùy chọn được tạo động, bạn có thể dùng parseUrlHttps để phân tích chuỗi URL:
fetchWithAuth :: IO ()
fetchWithAuth = runReq defaultHttpConfig $ do
let (url, baseOptions) = parseUrlHttps "https://httpbin.org/get?mode=debug"
& fromMaybe (error "Invalid URL")
customOpts = "from" =: (10 :: Int)
<> "to" =: (20 :: Int)
<> basicAuth "user" "pass"
<> baseOptions
<> port 443
response <- req GET url NoReqBody jsonResponse customOpts
liftIO $ print (responseBody response :: Value)
POST với dữ liệu JSON
Để gửi dữ liệu JSON, định nghĩa kiểu dữ liệu và triển khai ToJSON, FromJSON:
{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson
import GHC.Generics
data Payload = Payload
{ count :: Int
, label :: String
} deriving (Show, Generic)
instance ToJSON Payload
instance FromJSON Payload
sendJsonPost :: IO ()
sendJsonPost = runReq defaultHttpConfig $ do
let payload = Payload 42 "demo-label"
result <- req POST (https "httpbin.org" /: "post")
(ReqBodyJson payload)
jsonResponse
mempty
liftIO $ print (responseBody result :: Value)
POST với form-urlencoded
Gửi dữ liệu dưới dạng form:
sendForm :: IO ()
sendForm = runReq defaultHttpConfig $ do
let formData = "name" =: ("Alice" :: Text)
<> "active" =: True
<> queryFlag "verbose"
res <- req POST (https "httpbin.org" /: "post")
(ReqBodyUrlEnc formData)
jsonResponse
mempty
liftIO $ print (responseBody res :: Value)
Tương tác với JSONPlaceholder API
Sử dụng Req để làm việc với API giả lập jsonplaceholder.typicode.com.
data Post = Post
{ userId :: Int
, postId :: Int
, title :: Text
, content :: Text
} deriving (Show, Generic)
instance FromJSON Post
instance ToJSON Post
getSinglePost :: Int -> IO ()
getSinglePost n = runReq defaultHttpConfig $ do
resp <- req GET (https "jsonplaceholder.typicode.com" /: "posts" /~ n)
NoReqBody
jsonResponse
mempty
liftIO $ print (responseBody resp :: Post)
createNewPost :: IO ()
createNewPost = runReq defaultHttpConfig $ do
let newPost = Post 11 0 "Haskell Req" "Learning HTTP in pure functional style"
resp <- req POST (https "jsonplaceholder.typicode.com" /: "posts")
(ReqBodyJson newPost)
jsonResponse
mempty
liftIO $ print (responseBody resp :: Post)
modifyPost :: Int -> IO ()
modifyPost id = runReq defaultHttpConfig $ do
let updated = Post 99 0 "Updated Title" "Modified content"
resp <- req PUT (https "jsonplaceholder.typicode.com" /: "posts" /~ id)
(ReqBodyJson updated)
jsonResponse
mempty
liftIO $ print (responseBody resp :: Post)
removePost :: Int -> IO ()
removePost id = runReq defaultHttpConfig $ do
_ <- req DELETE (https "jsonplaceholder.typicode.com" /: "posts" /~ id)
NoReqBody
ignoreResponse
mempty
liftIO $ putStrLn $ "Deleted post " ++ show id
Phân tích hàm chính: req và runReq
Hàm req là lõi của thư viện, với chữ ký kiểu mạnh:
req :: (MonadHttp m, HttpMethod method, HttpBody body, HttpResponse response)
=> method
-> Url scheme
-> body
-> Proxy response
-> Option scheme
-> m response
- method: Phương thức HTTP như
GET,POST, v.v. - Url: Được xây dựng bằng toán tử
/:(dành cho đoạn path cố định) và/~(cho giá trị động). - body: Dữ liệu gửi đi, ví dụ
NoReqBody,ReqBodyJson data. - Proxy response: Chỉ định cách xử lý phản hồi —
bsResponse,jsonResponse. - Option: Các tuỳ chọn như header, tham số truy vấn, xác thực.
Hàm runReq thực thi ngữ cảnh Req và trả về kết quả:
runReq :: MonadIO m => HttpConfig -> Req a -> m a
Bạn thường dùng defaultHttpConfig (từ Data.Default.Class) để sử dụng cấu hình mặc định, bao gồm tự động đọc proxy từ biến môi trường HTTP_PROXY.
Kết luận
Thư viện req cung cấp một cách tiếp cận kiểu-an-toàn và sạch sẽ để thực hiện HTTP request trong Haskell. Với cú pháp rõ ràng, hỗ trợ JSON tích hợp và khả năng mở rộng cao, nó rất phù hợp cho cả ứng dụng nhỏ lẫn dự án quy mô lớn.