כתיבת יישומי אינטרנט של קלוז'ר עם טבעת

1. הקדמה

טבעת היא ספרייה לכתיבת יישומי אינטרנט בקלוז'ור. הוא תומך בכל הדרוש לכתיבת אפליקציות אינטרנט בעלות תכונות מלאות ויש לו מערכת אקולוגית משגשגת כדי להפוך אותה לחזקה עוד יותר.

במדריך זה אנו נותנים מבוא לרינג ונראה כמה מהדברים שאנו יכולים להשיג באמצעותו.

Ring אינו מסגרת המיועדת ליצירת ממשקי API של REST, כמו כל כך הרבה ערכות כלים מודרניות. זו מסגרת ברמה נמוכה יותר לטיפול בבקשות HTTP באופן כללי, עם דגש על פיתוח אתרים מסורתי. עם זאת, ספריות מסוימות מתבססות עליו על מנת לתמוך במבני יישומים רבים אחרים.

2. תלות

לפני שנוכל להתחיל לעבוד עם Ring, עלינו להוסיף אותו לפרויקט שלנו. התלות המינימלית שאנחנו צריכים הם:

  • טבעת / טבעת-ליבת
  • טבעת / מתאם טבעת-מזח

אנו יכולים להוסיף אותם לפרויקט ליינינגן שלנו:

 : תלות [[org.clojure / clojure "1.10.0"] [טבעת / טבעת ליבת "1.7.1"] [טבעת / מתאם טבעת-רציף "1.7.1"]]

לאחר מכן נוכל להוסיף זאת לפרויקט מינימלי:

(ns ring.core (: use ring.adapter.jetty)) (מטפל defn [בקשה] {: סטטוס 200: כותרות {"סוג תוכן" "טקסט / רגיל"}: גוף "שלום עולם"}) (defn - ראשי [& args] (מטפל run-jetty {: port 3000}))

כאן הגדרנו פונקציית מטפל - אותה נסקור בקרוב - שתמיד מחזירה את המחרוזת "שלום עולם". כמו כן, הוספנו את הפונקציה העיקרית שלנו לשימוש במטפל זה - הוא יקשיב לבקשות ביציאה 3000.

3. מושגי ליבה

לליינינגן יש כמה מושגי ליבה שסביבם הכל נבנה: בקשות, תגובות, מטפלים ו- Middleware.

3.1. בקשות

בקשות הן ייצוג של בקשות HTTP נכנסות. טבעת מייצגת בקשה כמפה, המאפשרת ליישום Clojure שלנו לתקשר עם השדות הנפרדים בקלות. יש סט מקשים סטנדרטי במפה זו, כולל אך לא מוגבל ל:

  • : אורי - נתיב ה- URI המלא.
  • : מחרוזת שאילתה - מחרוזת השאילתות המלאה.
  • : שיטת בקשה - שיטת הבקשה, אחת מ- : קבל,: ראש,: פוסט,: שים,: מחק אוֹ :אפשרויות.
  • : כותרות - מפה של כל כותרות ה- HTTP שסופקו לבקשה.
  • :גוּף - InputStream מייצג את גוף הבקשה, אם קיים.

Middleware עשוי להוסיף גם מפתחות נוספים למפה זו לפי צורך.

3.2. תגובות

באופן דומה, התגובות הן ייצוג של תגובות ה- HTTP היוצאות. טבעת מייצגת את אלה גם כמפות עם שלושה מקשים סטנדרטיים:

  • :סטָטוּס - קוד הסטטוס שיש לשלוח בחזרה
  • :כותרות - מפה של כל כותרות ה- HTTP שיש לשלוח בחזרה
  • :גוּף - הגוף האופציונלי לשלוח בחזרה

כמו קודם, Middleware עשויה לשנות זאת בין המטפל שלנו שמייצר אותו לבין התוצאה הסופית שתישלח ללקוח.

טבעת מספקת גם כמה עוזרים בכדי להקל על בניית התגובות.

הבסיסי ביותר מבין אלה הוא ה- ring.util. תגובה / תגובה פונקציה, היוצרת תגובה פשוטה עם קוד סטטוס של 200 בסדר:

ring.core => (ring.util.response / response "Hello") {: status 200,: headers {},: body "Hello"}

ישנן כמה שיטות נוספות המתאימות לכך לקודי סטטוס נפוצים - לדוגמה, בקשה רעה, לא נמצא ו הפניה מחדש:

ring.core => (ring.util.response / bad-request "Hello") {: status 400,: headers {},: body "Hello"} ring.core => (ring.util.response / created "/ פוסט / 123 ") {: סטטוס 201,: כותרות {" מיקום "" / פוסט / 123 "},: גוף אפס} ring.core => (ring.util.response / redirect" //ring-clojure.github. io / ring / ") {: status 302,: headers {" Location "" //ring-clojure.github.io/ring/ "},: body" "}

יש לנו גם את סטָטוּס שיטה שתמיר תגובה קיימת לכל קוד סטטוס שרירותי:

ring.core => (ring.util.response / status (ring.util.response / response "Hello") 409) {: status 409,: headers {},: body "Hello"}

יש לנו כמה שיטות להתאים תכונות אחרות של התגובה באופן דומה - לדוגמה, סוג תוכן, כותרת אוֹ סט-עוגיה:

ring.core => (ring.util.response / type-type (ring.util.response / response "Hello") "text / plain") {: status 200,: headers {"Type-Type" "text / plain "},: body" Hello "} ring.core => (ring.util.response / header (ring.util.response / response" Hello ")" X-Tutorial-For "" Baeldung ") {: status 200, : כותרות {"X-Tutorial-For" "Baeldung"},: body "Hello"} ring.core => (ring.util.response / set-cookie (ring.util.response / response "Hello") "משתמש "" 123 ") {: status 200,: headers {},: body" Hello ",: cookies {" User "{: value" 123 "}}}

ציין זאת ה סט-עוגיה השיטה מוסיפה ערך חדש לגמרי למפת התגובה. זה צריך את עוגיות עטיפה תוכנת ביניים לעבד את זה בצורה נכונה כדי שזה יעבוד.

3.3. מטפלים

כעת, לאחר שאנו מבינים בקשות ותגובות, אנו יכולים להתחיל לכתוב את פונקציית המטפל שלנו כדי לקשור אותה יחד.

מטפל הוא פונקציה פשוטה שלוקחת את הבקשה הנכנסת כפרמטר ומחזירה את התגובה היוצאת. מה שאנחנו עושים בפונקציה זו תלוי לחלוטין ביישום שלנו, כל עוד זה מתאים לחוזה זה.

בפשטות ביותר נוכל לכתוב פונקציה שתמיד מחזירה את אותה התגובה:

(מטפל defn [בקשה] (ring.util.response / תגובה "שלום"))

אנו יכולים לקיים אינטראקציה עם הבקשה גם לפי הצורך.

לדוגמה, נוכל לכתוב מטפל להחזרת כתובת ה- IP הנכנסת:

(defn check-ip-handler [בקשה] (ring.util.response / סוג תוכן (ring.util.response / תגובה (: בקשה addr מרחוק)) "טקסט / רגיל"))

3.4. Middleware

Middleware הוא שם המקובל בשפות מסוימות אך פחות בעולם Java. מבחינה רעיונית הם דומים למסנני סרוולט ומיירטים באביב.

ב- Ring, תוכנת ביניים מתייחסת לפונקציות פשוטות שעוטפות את המטפל הראשי ומתאימות כמה היבטים שלו בצורה כלשהי. המשמעות של זה היא מוטציה של הבקשה הנכנסת לפני שהיא מעובדת, מוטציה של התגובה היוצאת לאחר שנוצרה או שעושה פוטנציאל לא יותר מאשר לרשום כמה זמן לקח לעיבוד.

בכללי, פונקציות התווך לוקחות פרמטר ראשון של המטפל לעטוף ומחזיר פונקציית מטפל חדשה עם הפונקציונליות החדשה.

תוכנת הביניים יכולה להשתמש בכמה שיותר פרמטרים לפי הצורך. לדוגמה, נוכל להשתמש בהגדרות הבאות כדי להגדיר את סוג תוכן כותרת על כל תגובה מהמטפל העטוף:

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

כשקוראים אותה נוכל לראות שאנחנו מחזירים פונקציה שלוקחת בקשה - זה המטפל החדש. פעולה זו תקרא למטפל שסופק ואז תחזיר גרסה מוטציה של התגובה.

אנו יכולים להשתמש בזה כדי לייצר מטפל חדש פשוט על ידי שרשור ביניהם:

(def אפליקציה מטפלת (מטפלת מסוג עטיפת תוכן "טקסט / html"))

Clojure מציע גם דרך לשרשר רבים באופן טבעי יותר - על ידי שימוש במקרו השחלה. אלו הן דרך לספק רשימת פונקציות להתקשרות, כל אחת עם הפלט של הקודמת.

בפרט, אנו רוצים את המאקרו Thread First, ->. זה יאפשר לנו להתקשר לכל תוכנת ביניים עם הערך המסופק כפרמטר הראשון:

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

זה יצר אז מטפל שהוא המטפל המקורי עטוף בשלוש פונקציות אמצעי תוכנה שונים.

4. מטפלי כתיבה

כעת, לאחר שהבנו את הרכיבים המרכיבים יישום טבעת, עלינו לדעת מה אנו יכולים לעשות עם המטפלים בפועל. אלה הם הלב של היישום כולו וכאן רוב ההיגיון העסקי ילך.

אנו יכולים להכניס את כל הקוד שנרצה למטפלים אלה, כולל גישה למסד נתונים או התקשרות לשירותים אחרים. טבעת נותנת לנו כמה יכולות נוספות לעבודה ישירה עם הבקשות הנכנסות או התגובות היוצאות שמועילות מאוד גם כן.

4.1. הגשת משאבים סטטיים

אחת הפונקציות הפשוטות ביותר שכל יישום אינטרנט יכול לבצע היא לשרת משאבים סטטיים. טבעת מספקת שתי פונקציות אמצעי תוכנה כדי להקל על כך - קובץ גלישה ו משאב לעטוף.

ה קובץ גלישה middleware לוקח ספרייה במערכת הקבצים. אם הבקשה הנכנסת תואמת לקובץ בספריה זו, קובץ זה מוחזר במקום להתקשר לפונקציית המטפל:

(השתמש ב- 'ring.middleware.file) 
(def app-handler (wrap-file your handler handler "/ var / www / public"))

באופן דומה מאוד, ה משאב לעטוף Middleware לוקח קידומת שביל בכיתה בה הוא מחפש את הקבצים:

(השתמש ב- 'ring.middleware.resource') 
(def app-handler (wrap-resource your handler handler "public"))

בשני המקרים, פונקציית המטפל העטוף נקראת רק אם לא נמצא קובץ שיחזור ללקוח.

טבעת מספקת גם תוכנת תיווך נוספת כדי להפוך אותם לנקיים יותר לשימוש באמצעות ה- API של HTTP:

(השתמש ב- 'ring.middleware.resource' ring.middleware.content-type 'ring.middleware.not-modified) (def app-handler (-> your-handler (wrap-resource "public") wrap-type-type wrap) לא השתנה)

ה לעטוף תוכן-סוג תוכנת האמצעי תקבע באופן אוטומטי את סוג תוכן כותרת להגדרה בהתבסס על סיומת שם הקובץ המבוקשת. ה לעטוף-לא שונה תוכנת הביניים משווה את אם לא השתנה כותרת אל שונה לאחרונה ערך לתמיכה במטמון HTTP, רק להחזיר את הקובץ במידת הצורך.

4.2. גישה לפרמטרים של בקשה

בעת עיבוד בקשה, ישנן כמה דרכים חשובות בהן הלקוח יכול לספק מידע לשרת. אלה כוללים פרמטרים של מחרוזת שאילתה - הכלולים בפרמטרים של כתובת ה- URL והטופס - שנשלחו כטען הבקשה לבקשות POST ו- PUT.

לפני שנוכל להשתמש בפרמטרים, עלינו להשתמש ב- פרמים עוטפים תוכנת אמצעי לעטוף את המטפל. זה מנתח כהלכה את הפרמטרים, תומך בקידוד כתובות אתרים והופך אותם לזמינים לבקשה. זה יכול אופציונלי לציין את קידוד התווים לשימוש, ברירת מחדל ל- UTF-8 אם לא צוין:

(def app-handler (-> your-handler (wrap-params {: קידוד "UTF-8"})))

לאחר שתסיים, הבקשה תתעדכן בכדי להפוך את הפרמטרים לזמינים. אלה נכנסים למפתחות מתאימים בבקשה הנכנסת:

  • : שאילתות-פאראמים - הפרמטרים המנותחים ממחרוזת השאילתה
  • : טפסים פאראמים - הפרמטרים המנותחים מגוף הטופס
  • : פאראמים - השילוב של שניהם : שאילתות-פאראמים ו : טפסים פאראמים

אנו יכולים לעשות זאת שימוש אצל מטפל הבקשות שלנו בדיוק כצפוי.

(defn-echo handler [{params: params}] (ring.util.response / type-type (ring.util.response / response (get params "input")) "text / plain"))

מטפל זה יחזיר תגובה המכילה את הערך מהפרמטר קֶלֶט.

פרמטרים ממפים למחרוזת אחת אם קיים רק ערך אחד, או לרשימה אם קיימים ערכים מרובים.

לדוגמה, אנו מקבלים את מפות הפרמטרים הבאות:

// / echo? input = hello {"input" hello "} // / echo? input = hello & name = Fred {" input "hello" "name" "Fred"} // / echo? input = hello & input = world {" קלט ["שלום" "עולם"]}

4.3. קבלת העלאות קבצים

לעתים קרובות אנו רוצים להיות מסוגלים לכתוב יישומי אינטרנט שמשתמשים יכולים להעלות אליהם קבצים. בפרוטוקול HTTP, זה מטופל בדרך כלל באמצעות בקשות מרובות חלקים. אלה מאפשרים בקשה אחת להכיל גם פרמטרים של טופס וגם קבוצה של קבצים.

הטבעת מגיעה עם תוכנת אמצע שנקראת לעטוף-רב-פארמים לטפל בבקשה מסוג זה. זה דומה לאופן שבו פרמים עוטפים מנתח בקשות פשוטות.

לעטוף-רב-פארמים מפענח ומאחסן באופן אוטומטי את כל הקבצים שהועלו למערכת הקבצים ואומר למטפל היכן הוא אמור לעבוד איתם:

(def app-handler (-> your-handler wrap-params wrap-multipart-params))

כברירת מחדל, הקבצים שהועלו נשמרים בספריית המערכת הזמנית ונמחקים אוטומטית לאחר שעה. שים לב שזה אכן מחייב ש- JVM עדיין פועל במשך השעה הבאה כדי לבצע את הניקוי.

אם מעדיפים, יש גם חנות בזיכרוןאם כי ברור, הדבר עלול להיגמר הזיכרון אם קבצים גדולים מועלים.

אנו יכולים גם לכתוב את מנועי האחסון שלנו במידת הצורך, כל עוד הוא ממלא את דרישות ה- API.

(def app-handler (-> your-handler wrap-params (wrap-multipart-params {: store ring.middleware.multipart-params.byte-array / byte-array-store})))

לאחר הגדרת התווך הזה, הקבצים שהועלו זמינים באובייקט הבקשה הנכנסת תחת ה- פאראמים מַפְתֵחַ. זה זהה לשימוש ב- פרמים עוטפים תוכנת ביניים. ערך זה הוא מפה המכילה את הפרטים הדרושים לעבודה עם הקובץ, תלוי בחנות בה נעשה שימוש.

לדוגמה, חנות הקבצים הזמנית המוגדרת כברירת מחדל מחזירה ערכים:

 {"קובץ" {: שם קובץ "words.txt": סוג תוכן "טקסט / רגיל": tempfile #object [java.io.File ...]: גודל 51}}

איפה ה : tempfile הכניסה היא java.io. קובץ אובייקט המייצג ישירות את הקובץ במערכת הקבצים.

4.4. עבודה עם עוגיות

קובצי Cookie הם מנגנון שבו השרת יכול לספק כמות קטנה של נתונים שהלקוח ימשיך לשלוח בחזרה בבקשות הבאות. זה משמש בדרך כלל עבור מזהי הפעלות, אסימוני גישה או נתוני משתמש קבועים כגון הגדרות הלוקליזציה שהוגדרו.

לטבעת יש תוכנת ביניים שתאפשר לנו לעבוד עם עוגיות בקלות. זה ינתח אוטומטית קובצי Cookie על בקשות נכנסות, וגם יאפשר לנו ליצור קובצי Cookie חדשים בתגובות יוצאות.

קביעת תצורה של תוכנת ביניים זו פועלת לפי אותם דפוסים כמו בעבר:

(def-handler app (-> עוגיות העטיפה שלך-handler))

בנקודה זו, כל הבקשות הנכנסות ינתחו את העוגיות שלהן ויוכנסו לתוך :עוגיות הקלד את הבקשה. זה יכיל מפה של שם הקובץ והערך:

{"session_id" {: value "session-id-hash"}}

לאחר מכן נוכל להוסיף עוגיות לתגובות יוצאות על ידי הוספת ה- :עוגיות מפתח לתגובה היוצאת. אנו יכולים לעשות זאת על ידי יצירת התגובה ישירות:

{: status 200: headers {}: cookies {"session_id" {: value "session-id-hash"}}: body "הגדרת קובץ cookie."}

יש גם פונקציית עוזר שבה נוכל להשתמש כדי להוסיף עוגיות לתגובות, באופן דומה למה שקודם לכן היינו יכולים להגדיר קודי מצב או כותרות:

(ring.util.response / set-cookie (ring.util.response / response "הגדרת קובץ cookie.") "session_id" "session-id-hash")

לעוגיות יכולות להיות מוגדרות אפשרויות נוספות, לפי הצורך במפרט HTTP. אם אנחנו משתמשים סט-עוגיה ואז אנו מספקים אותם כפרמטר של מפה אחרי המפתח והערך. המפתחות למפה זו הם:

  • :תְחוּם - הדומיין שאליו תוכל להגביל את העוגיה
  • :נָתִיב - הדרך להגביל את העוגיה
  • :לבטחנָכוֹן לשלוח רק את העוגיה בחיבורי HTTPS
  • : http בלבדנָכוֹן כדי להפוך את העוגיה לנגישה ל- JavaScript
  • : מקסימום גיל - מספר השניות שלאחריהן הדפדפן מוחק את העוגיה
  • : יפוג - חותמת זמן ספציפית שלאחריה הדפדפן מוחק את העוגיה
  • : אותו אתר - אם מוגדר ל :קַפְּדָנִי, ואז הדפדפן לא ישלח קובץ cookie זה עם בקשות חוצות אתרים.
(ring.util.response / set-cookie (ring.util.response / response "הגדרת קובץ cookie.") "session_id" "session-id-hash" {: secure true: http-only true: max-age 3600} )

4.5. מושבים

עוגיות נותנות לנו את היכולת לאחסן פיסות מידע שהלקוח שולח בחזרה לשרת בכל בקשה. דרך חזקה יותר להשיג זאת היא שימוש בפגישות. אלה מאוחסנים לחלוטין בשרת, אך הלקוח שומר על המזהה שקובע באיזו הפעלה להשתמש.

כמו בכל השאר כאן, הפעלות מיושמות באמצעות פונקציית אמצעי תוכנה:

(def app-handler (-> הפעלת העטיפה שלך של המטפל))

כברירת מחדל, זה שומר את נתוני ההפעלה בזיכרון. אנו יכולים לשנות זאת במידת הצורך, ורינג מגיעה עם חנות חלופית המשתמשת בעוגיות לאחסון כל נתוני ההפעלה.

כמו בהעלאת קבצים, אנו יכולים לספק את פונקציית האחסון שלנו במידת הצורך.

(def app-handler (-> cookie-wrap שלך של המטפל (wrap-session {: store (cookie-store {: key "a 16-by secret")))))

אנו יכולים גם להתאים את פרטי העוגיה המשמשת לאחסון מפתח ההפעלה.

לדוגמה, כדי לגרום לכך שקובץ ה- cookie של ההפעלה יימשך שעה אחת נוכל לעשות:

(def-handler app (-> cookie-wrap שלך של המטפל (session-session {: cookie-attrs {: max-age 3600}})))

מאפייני העוגיה כאן זהים לתמיכת ה- עוגיות עטיפה תוכנת ביניים.

מושבים יכולים לרוב לשמש כמאגרי נתונים לעבוד איתם. זה לא תמיד עובד טוב במודל תכנות פונקציונלי, כך שהטבעת מיישמת אותם מעט אחרת.

במקום זאת, אנו ניגשים לנתוני ההפעלה מהבקשה, ומחזירים מפת נתונים לאחסון בתוכם כחלק מהתגובה. זהו כל מצב ההפעלה לאחסון, לא רק הערכים שהשתנו.

לדוגמה, הדברים הבאים מפקחים על מספר הפעמים שהתבקש המטפל:

(defn handler [{session: session}] (let [count (: count session 0) session (assoc session: count (inc count)))] - -> (תגובה (str "ניגשת לדף זה" לספור "פעמים." )) (assoc: מושב מושב))))

לעבוד ככה, אנחנו יכולים הסר נתונים מהפגישה פשוט על ידי לא כולל את המפתח. אנחנו יכולים גם למחוק את כל ההפעלה על ידי חזרה אֶפֶס למפה החדשה.

(מטפל defn [בקשה] (-> (תגובה "מושב נמחק.") (assoc: הפעלה אפס)))

5. תוסף ליינינגן

Ring מספק תוסף לכלי הבנייה של ליינינגן כדי לסייע גם בפיתוח וגם בייצור.

הגדרנו את התוסף על ידי הוספת פרטי התוסף הנכונים ל- project.clj קוֹבֶץ:

 : תוספים [[lein-ring "0.12.5"]]: ring {: handler ring.core / handler}

חשוב שהגרסה של טבעת lein נכון לגרסה של רינג. כאן השתמשנו בטבעת 1.7.1, מה שאומר שאנחנו צריכים טבעת lein 0.12.5. באופן כללי, זה הכי בטוח להשתמש רק בגרסה האחרונה של שניהם, כפי שנראה במרכז Maven או עם חיפוש lein פקודה:

$ lein search ring-core חיפוש מחסנים ... [ring / ring-core "1.7.1"] ספריות ליבה טבעתיות. $ lein search lein-ring מחפש באדמות ... [lein-ring "0.12.5"] תוסף טבעת ליינינגן

ה : מטפל פרמטר ל :טַבַּעַת call הוא השם המלא של המטפל בו אנו רוצים להשתמש. זה יכול לכלול כל תוכנת ביניים שהגדרנו.

השימוש בתוסף זה אומר שאנחנו כבר לא צריכים פונקציה מרכזית. אנו יכולים להשתמש ב- Leiningen להפעלת מצב פיתוח, או אחרת נוכל לבנות חפץ ייצור למטרות פריסה. הקוד שלנו מסתכם כעת בהיגיון שלנו ותו לא.

5.1. בניית חפץ הפקה

לאחר הגדרת זה, כעת נוכל לבנות קובץ WAR שנוכל לפרוס לכל מיכל סרוולט רגיל:

$ טבעת lein מעל מלחמה 2019-04-12 07: 10: 08.033: INFO :: main: רישום התחיל @ 1054ms ל- org.eclipse.jetty.util.log.StdErrLog נוצר ./clojure/ring/target/uberjar/ring-0.1 .0-SNAPSHOT-עצמאי.מלחמה

אנחנו יכולים גם לבנות קובץ JAR עצמאי שיפעיל את המטפל שלנו בדיוק כצפוי:

$ טבעת lein uberjar Compiling ring.core 2019-04-12 07: 11: 27.669: INFO :: main: רישום התחיל @ 3016ms ל- org.eclipse.jetty.util.log.StdErrLog נוצר ./clojure/ring/target/uberjar /ring-0.1.0-SNAPSHOT.jar נוצר ./clojure/ring/target/uberjar/ring-0.1.0-SNAPSHOT-standalone.jar

קובץ JAR זה יכלול מחלקה ראשית שתתחיל את המטפל במיכל המשובץ שכללנו. זה גם יכבד משתנה סביבתי של נמל המאפשר לנו להפעיל אותו בקלות בסביבת ייצור:

PORT = 2000 java -jar ./clojure/ring/target/uberjar/ring-0.1.0-SNAPSHOT-standalone.jar 2019-04-12 07: 14: 08.954: INFO :: main: רישום התחיל @ 1009ms לארגון. eclipse.jetty.util.log.StdErrLog אזהרה: ניתן להגדיר? כבר מתייחס ל: # 'clojure.core / seqable? במרחב השמות: clojure.core.incubator, מוחלף על ידי: # 'clojure.core.incubator / seqable? 2019-04-12 07: 14: 10.795: מידע: oejs. שרת: ראשי: jetty-9.4.z-SNAPSHOT; בנוי: 2018-08-30T13: 59: 14.071Z; git: 27208684755d94a92186989f695db2d7b21ebc51; jvm 1.8.0_77-b03 2019-04-12 07: 14: 10.863: INFO: oejs.AbstractConnector: main: התחיל [מוגן באמצעות דוא"ל] {HTTP / 1.1, [http / 1.1]} {0.0.0.0:2000} 2019- 04-12 07: 14: 10.863: מידע: oejs. שרת: ראשי: התחיל @ 2918 ms השרת התחיל ביציאה 2000

5.2. פועל במצב פיתוח

למטרות פיתוח, אנו יכולים להפעיל את המטפל ישירות מלינינגן מבלי שנצטרך לבנות ולהפעיל אותו באופן ידני. זה מקל על הבדיקה של היישום שלנו בדפדפן אמיתי:

שרת טבעת $ lein 2019-04-12 07: 16: 28.908: INFO :: main: רישום התחיל @ 1403ms ל- org.eclipse.jetty.util.log.StdErrLog 2019-04-12 07: 16: 29.026: INFO: oejs .שרת: ראשי: jetty-9.4.12.v20180830; בנוי: 2018-08-30T13: 59: 14.071Z; git: 27208684755d94a92186989f695db2d7b21ebc51; jvm 1.8.0_77-b03 12-04-2019 07: 16: 29.092: מידע: oejs.AbstractConnector: ראשי: התחיל [מוגן באמצעות דוא"ל] {HTTP / 1.1, [http / 1.1]} {0.0.0.0:3000} 2019- 04-12 07: 16: 29.092: מידע: oejs. שרת: ראשי: התחיל @ 1587ms

זה גם מכבד את נמל משתנה הסביבה אם הגדרנו זאת.

בנוסף, יש ספריית פיתוח טבעות שנוכל להוסיף לפרויקט שלנו. אם זה זמין, אז שרת הפיתוח ינסה לטעון מחדש באופן אוטומטי את כל שינויי המקור שזוהו. זה יכול לתת לנו זרימת עבודה יעילה של שינוי הקוד וראייתו בשידור חי בדפדפן שלנו. זה דורש את טבעת-התפתחות הוספת תלות:

[טבעת / טבעת-התפתחות "1.7.1"]

6. מסקנה

במאמר זה נתנו הקדמה קצרה לספריית הטבעת כאמצעי לכתוב יישומי אינטרנט ב- Clojure. למה לא לנסות את זה בפרויקט הבא?

דוגמאות לכמה מהמושגים שסיקרנו כאן ניתן לראות ב- GitHub.