05 - 使用 AJAX 通信

01 - Clojure Web 程序基本結(jié)構(gòu).png

客戶端使用 Ajax

引入依賴

;; Ajax 庫
[cljs-ajax "0.7.4"]

;; 支持 JSON 格式的中間件
[ring-middleware-format "0.7.2"]

配置中間件

添加支持 JSON 格式的中間件(本段講解红碑,沒有代碼)

注意:因?yàn)槲覀儗?huì)為客戶端 Ajax 請(qǐng)求設(shè)置 :format :json 選項(xiàng),因此服務(wù)端收到的請(qǐng)求參數(shù)將會(huì)是下面這樣:

:params {email jiesoul@gmail.com, password 12345678}

而我們希望的格式應(yīng)該如下:

:params {:email jiesoul@gmail.com, :password 12345678}

解決方法是開啟 ring-middleware-format 中間件的 :formats [:json-kw] 選項(xiàng)泡垃,他會(huì)自動(dòng)為 JSON 數(shù)據(jù)設(shè)置關(guān)鍵字

(wrap-format/wrap-restful-format :formats [:json-kw])

代碼

文件:src/soul_talk/core.clj

(ns soul-talk.core
  (:require 
    ......
    ;; 支持 JSON 格式的中間件
    [ring.middleware.format :as wrap-format]))


(def app
  (-> app-routes  
      (wrap-nocache)
      (wrap-reload)
      (wrap-webjars) 
      
      ;; 這行是添加的
      (wrap-format/wrap-restful-format :formats [:json-kw])

      (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))

(defn -main []
  (jetty/run-jetty app {:port 3000 :join? false}))

ClojureScript

改造 login.cljs 析珊,加入 Ajax 功能

主要進(jìn)行了以下幾項(xiàng)修改

  • 創(chuàng)建了一個(gè) login-data 變量,保存客戶端登陸數(shù)據(jù)蔑穴,他會(huì)通過 Ajax 被發(fā)送到服務(wù)端
  • 之前輸入框丟失焦點(diǎn)后忠寻,僅僅將輸入框和要使用的驗(yàn)證函數(shù)傳給 validate-invalid ;現(xiàn)在還需要向 login-data 變量中添加數(shù)據(jù)
  • 之前輸入框丟失焦點(diǎn)后存和,直接通過 id 獲取組件 奕剃,現(xiàn)在則是通過事件對(duì)象 e 獲得組件
  • 之前點(diǎn)擊提交按鈕后,validate-form 直接從數(shù)據(jù)框中讀取數(shù)據(jù)進(jìn)行驗(yàn)證捐腿,現(xiàn)在從 JSON 變量中讀取數(shù)據(jù)進(jìn)行驗(yàn)證
  • 之前點(diǎn)擊提交按鈕纵朋,驗(yàn)證成功后,返回 true 茄袖,然后提交到服務(wù)器操软;現(xiàn)在驗(yàn)證成功后,通過 Ajax 提交數(shù)據(jù)宪祥,頁面不刷新
  • 把客戶端頁面的 form 改為 div 元素

==注意:服務(wù)端登錄無論成功還是失敗聂薪,最后 Ajax 都會(huì)調(diào)用 haddler-ok 函數(shù)猪钮,那是因?yàn)榉?wù)端返回的狀態(tài)碼被轉(zhuǎn)換成了 JSON 數(shù)據(jù) {"status":404,"errors":"用戶名密碼不對(duì)"},而狀態(tài)碼總是 200胆建,后面會(huì)改進(jìn)這個(gè)問題==

修改 login.cljs

(ns soul-talk.login
  (:require [domina :as dom]
            [domina.events :as ev]
            [reagent.core :as reagent :refer [atom]]
            ;; 引入共享代碼
            [soul-talk.auth-validate :as validate]
            ;; 引入 Ajax 支持
            [ajax.core :as ajax]))


(def login-data (atom {:email "" :password ""}))


;; 如果驗(yàn)證不成功烤低,則在輸入框上增加樣式;
;; 如果驗(yàn)證成功笆载,則移除樣式
;; 這個(gè)函數(shù)扑馁,輸入框失去焦點(diǎn)的時(shí)候被調(diào)用
(defn validate-invalid [input vali-fun]
  (if-not (vali-fun (.-value input))  ;; 驗(yàn)證函數(shù)傳入文本,而不是 HTML 元素
    (dom/add-class! input "is-invalid")
    (dom/remove-class! input "is-invalid")))


;; Ajax 成功后調(diào)用
(defn handler-ok [response]
  (js/alert @login-data))

;; Ajax 失敗后調(diào)用
(defn handler-error [{:keys [status status-text]}]
  (js/alert (sstr status status-text)))


(defn login! []
  (ajax/POST
    "/login"
    {:format        :json
     :headers       {"Accept" "application/transit+json"}
     :params        @login-data
     :handler       handler-ok
     :error-handler handler-error}))


;; 這個(gè)函數(shù)提交的時(shí)候被調(diào)用凉驻,類客戶端驗(yàn)證輸入格式是否正確
(defn validate-form []
  ;; 注意這里的變化
  ;; 數(shù)據(jù)不再是從元素中直接讀取腻要,而是從 JSON 數(shù)據(jù)中讀取
  (if (and (validate/validate-email (:email @login-data))
           (validate/validate-passoword (:password @login-data)))

    ;; 注意這里的變化:之前驗(yàn)證成功返回 true ,則表單可以提交
    ;; 現(xiàn)在是調(diào)用 login! 函數(shù)涝登,利用 ajax 從后臺(tái)讀取據(jù)雄家,不提交也不刷新頁面
    (login!)
    (do
      (js/alert "email和密碼不合法")
      false)))



;; 組件化登陸表單
(defn login-component []
  [:div.container
   ;; 登陸表單
   [:form#loginForm.form-signin
    ;; 標(biāo)題
    [:h1.h3.mb-3.font-weight-normal.text-center "Please sign in"]

    ;; Email 部分
    [:div.form-group
     ;; Email 標(biāo)簽
     [:label "Email address"]
     ;; Email 輸入框
     [:input#email.form-control
      {:type        "text"
       :name        "email"
       :auto-focus  true
       :placeholder "Email Address"
       ;; 焦點(diǎn)丟失的時(shí)候,調(diào)用驗(yàn)證函數(shù)
       :on-blur   (fn [e]
                      (let [d (.. e -target)]
                        (swap! login-data assoc :email (.-value d))
                        (validate-invalid d validate/validate-email)))}]
     ;; 錯(cuò)誤提示信息
     [:div.invalid-feedback "無效的 Email"]]


    ;; 密碼部分
    [:div.form-group
     [:label "Password"]
     ;; 密碼輸入框
     [:input#password.form-control
      {:type        "password"
       :name        "password"
       :placeholder "password"
       ;; 焦點(diǎn)丟失的時(shí)候胀滚,調(diào)用驗(yàn)證函數(shù)
       ;; 之前的代碼僅僅將元素和要使用的函數(shù)傳給 validate-invalid
       ;; 現(xiàn)在還需要向 JSON 中添加數(shù)據(jù)
       ;; 此外趟济,之前直接通過 id 獲取組件 ,現(xiàn)在則是通過事件對(duì)象 e 獲得組件
       :on-blur     (fn [e]
                      (let [d (.-target e)]
                        (swap! login-data assoc :password (.-value d))
                        (validate-invalid d validate/validate-passoword)))}]
     ;; 錯(cuò)誤提示信息
     [:div.invalid-feedback "無效的密碼"]]

    ;; “記住我” 復(fù)選框
    [:div.form-group.form-check
     [:input#rememeber.form-check-input {:type "checkbox"}]
     [:label "記住我"]]

    ;; 錯(cuò)誤信息
    [:div#error.invalid-feedback]

    ;; 提交按鈕
    [:input#submit.btn.btn-lg.btn-primary.btn-block
     {:type  "button"
      :value "登錄"
      :on-click #(validate-form)}]

    ;; 版權(quán)信息
    [:p.mt-5.mb-3.text-muted "&copy @2018"]]])



;; 渲染登陸表單組件咽笼,并掛載到 `content` div元素上
(defn load-page []
  (reagent/render
    [login-component]
    (dom/by-id "content")))


(defn ^:export init []
  (if (and js/document
           (.-getElementById js/document))
    (load-page)))


Clojure

完成服務(wù)端的 Ajax 支持

修改登錄 Handler顷编,和之前的代碼相比有以下變化:

  • 使用了共享代碼中的驗(yàn)證函數(shù)
  • request 中添加了 :session 字段,但是沒有任何意義剑刑,因?yàn)檫@個(gè)數(shù)據(jù)沒有傳輸給其他函數(shù)
  • 登陸成功媳纬,直接給客戶端返回了一個(gè) 200,其他處理有客戶端完成
  • 登陸失敗施掏,給客戶端返回 400 钮惠,但是要注意:這里的 400 是以 JSON 格式返回的,因此客戶端解析不出來
(ns soul-talk.core
  (:require
    ......
    ;; 響應(yīng)簡化庫七芭,這個(gè)庫暫時(shí)沒用了
    [ring.util.http-response :as resp]
    
    ;; 內(nèi)置響應(yīng)庫
    [ring.util.response :as res]
    
    ;; 引入共享代碼
    [soul-talk.auth-validate :as auth-validate]))
    
    
;; Post 登錄數(shù)據(jù)
(defn handle-login [{:keys [params] :as request}]
  (let [email (:email params)
        password (:password params)]
    (cond
      ;; 使用共享代碼中的函數(shù)素挽,之前沒有使用
      (not (auth-validate/validate-email email)) (res/response {:status 400 :errors "Email不合法"})
      (not (auth-validate/validate-passoword password)) (res/response {:status 400 :errors "密碼不合法"})
      (and (= email "jiesoul@gmail.com")
           (= password "12345678"))
      (do
        ;; 這行代碼沒任何意義
        (assoc-in request [:session :identity] email)
        
        ;; 僅僅給客戶端返回一個(gè) 200 狀態(tài)碼,有客戶端完成頁面跳轉(zhuǎn)
        (res/response {:status :ok}))
      
      :else (res/response {:status 400 :errors "用戶名密碼不對(duì)"}))))

修改路由

修改了 login 請(qǐng)求的 POST 路由抖苦,請(qǐng)求參數(shù)改成在 Hadler 中提然倭狻(好像意義不大)

(def app-routes
  (routes
    (GET "/" request (home-handle request))
    (GET "/about" [] (str "這是關(guān)于我的頁面"))
    (GET "/login" request (login-page request))
    
    ;; Post 路由修改
    ;; (POST "/login" [email password :as req] (handle-login email password req))
    (POST "/login" req (handle-login req)) 


    (GET "/logout" request (handle-logout request))
    (route/not-found error-page)))  
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锌历,隨后出現(xiàn)的幾起案子贮庞,更是在濱河造成了極大的恐慌,老刑警劉巖究西,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窗慎,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)遮斥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門峦失,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人术吗,你說我怎么就攤上這事尉辑。” “怎么了较屿?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵隧魄,是天一觀的道長。 經(jīng)常有香客問我隘蝎,道長购啄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任嘱么,我火速辦了婚禮狮含,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘曼振。我一直安慰自己几迄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布拴测。 她就那樣靜靜地躺著乓旗,像睡著了一般府蛇。 火紅的嫁衣襯著肌膚如雪集索。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天汇跨,我揣著相機(jī)與錄音务荆,去河邊找鬼。 笑死穷遂,一個(gè)胖子當(dāng)著我的面吹牛函匕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚪黑,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼盅惜,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了忌穿?” 一聲冷哼從身側(cè)響起抒寂,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掠剑,沒想到半個(gè)月后屈芜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年井佑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了属铁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躬翁,死狀恐怖焦蘑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盒发,我是刑警寧澤喇肋,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站迹辐,受9級(jí)特大地震影響蝶防,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜明吩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一间学、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧印荔,春花似錦低葫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至水泉,卻和暖如春善涨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背草则。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國打工钢拧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炕横。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓源内,卻偏偏與公主長得像,于是被迫代替她去往敵國和親份殿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子膜钓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)卿嘲。 注意:講述HT...
    kismetajun閱讀 27,449評(píng)論 1 45
  • Ajax 技術(shù) 第1章 認(rèn)識(shí)Ajax 1.1 初識(shí) ajax 我們平常上網(wǎng)颂斜,不管是注冊(cè)賬號(hào),還是瀏覽網(wǎng)頁腔寡,其本質(zhì)就...
    春風(fēng)之旅閱讀 2,888評(píng)論 0 26
  • Ajax 表單提交 在HTML中提供了表單提交的功能焚鲜,我們可以通過表單把數(shù)據(jù)從前臺(tái)提交到后臺(tái) 在HTML的DOM中...
    羊烊羴閱讀 708評(píng)論 0 4
  • 一、 認(rèn)識(shí)Ajax 1、 初識(shí) ajax 我們平常上網(wǎng)忿磅,不管是注冊(cè)賬號(hào)糯彬,還是瀏覽網(wǎng)頁,其本質(zhì)就是通過客戶端向服務(wù)器...
    寵辱不驚丶?xì)q月靜好閱讀 1,000評(píng)論 0 2
  • 你拿著醫(yī)院的診斷書回家, 口腔癌晚期吨些,我噙著淚送你遠(yuǎn)去搓谆, 你才年過半百,滿頭白發(fā)豪墅, 你蹣跚的身影清瘦泉手、凋零。 你喜...
    亞涓閱讀 2,476評(píng)論 47 113