duct.module.webのsiteとapiの違い

2023-05-18

ductのresource/config.ednの、duct.module/webとduct.module.web/apiとduct.module.web/siteの違いをメモ。

duct.module.webのsrc/duct/module/web.cljに、マルチメソッドが定義されています。

(defmethod ig/init-key :duct.module/web [_ options]
  #(apply-web-module % options base-config))

(defmethod ig/init-key ::api [_ options]
  #(apply-web-module % options api-config))

(defmethod ig/init-key ::site [_ options]
  #(apply-web-module % options (site-config (get-project-ns % options))))

違いはappky-web-moduleの引数であるbase-config、api-config、site-config

これらの違いは、以下。

baseapisite
:duct.handler.staticplane-test-responseマップ(json)html-response
:duct.middleware.web/defaults base-ring-defaults base-ring-defaults site-ring-defaults
その他のmiddlewareなしformatwebjars
(def ^:private base-config
  {:duct.handler.static/bad-request           (plaintext-response "Bad Request")
   :duct.handler.static/not-found             (plaintext-response "Not Found")
   :duct.handler.static/method-not-allowed    (plaintext-response "Method Not Allowed")
   :duct.handler.static/internal-server-error (plaintext-response "Internal Server Error")

   :duct.middleware.web/defaults base-ring-defaults
   :duct.handler/root
   {:middleware ^:distinct [(ig/ref :duct.middleware.web/not-found)
                            (ig/ref :duct.middleware.web/defaults)]}})
(def ^:private api-config
  {:duct.handler.static/bad-request           {:body ^:displace {:error :bad-request}}
   :duct.handler.static/not-found             {:body ^:displace {:error :not-found}}
   :duct.handler.static/method-not-allowed    {:body ^:displace {:error :method-not-allowed}}
   :duct.handler.static/internal-server-error {:headers {"Content-Type" "application/json"}
                                               :body    (core/resource "duct/module/web/errors/500.json")}

   :duct.middleware.web/format   {}                                       ;;
   :duct.middleware.web/defaults base-ring-defaults                       ;;
   :duct.handler/root
   {:middleware ^:distinct [(ig/ref :duct.middleware.web/not-found)
                            (ig/ref :duct.middleware.web/format)          ;;
                            (ig/ref :duct.middleware.web/defaults)]}})
(defn- site-config [project-ns]
  {:duct.handler.static/bad-request           (html-response error-400)
   :duct.handler.static/not-found             (html-response error-404)
   :duct.handler.static/method-not-allowed    (html-response error-405)
   :duct.handler.static/internal-server-error (html-response error-500)

   :duct.middleware.web/webjars               {}                               ;;
   :duct.middleware.web/defaults              (site-ring-defaults project-ns)  ;;
   :duct.handler/root
   {:middleware ^:distinct [(ig/ref :duct.middleware.web/not-found)
                            (ig/ref :duct.middleware.web/webjars)              ;;
                            (ig/ref :duct.middleware.web/defaults)]}})

:duct.middleware.web/defaults のbase-ring-defaultsとsite-ring-defaults の違いは以下。

ring-defaultsと比べると、
base-ring-defaultsは、ring.middleware.defaults/api-defaultsと同じ、
site-ring-defaults は ring.middleware.defaults/site-defaultsに:xss-protectionの追加と、:resources の変更をしたもの。

(def ^:private base-ring-defaults
  ^:demote {:params    {:urlencoded true, 
                        :keywordize true}
            :responses {:not-modified-responses true
                        :absolute-redirects true
                        :content-types true
                        :default-charset "utf-8"}})
(defn- site-ring-defaults [project-ns]
  ^:demote {:params    {:urlencoded true,
                        :multipart true,                                          ;;
                        :nested true,                                             ;;
                        :keywordize true}
            :cookies   true                                                       ;; ↓
            :session   {:flash true, 
                        :cookie-attrs {:http-only true, 
                                       :same-site :strict}}
            :security  {:anti-forgery         true
                        :xss-protection       {:enable? true,                     ;; +
                                               :mode :block}                      ;; +
                        :frame-options        :sameorigin
                        :content-type-options :nosniff}
            :static    {:resources ["duct/module/web/public"                      ;; +
                                    (str (name-to-path project-ns) "/public")]}   ;; ↑
            :responses {:not-modified-responses true
                        :absolute-redirects true
                        :content-types true
                        :default-charset "utf-8"}})

あと、apiとsiteのmiddlewareの違い。

wrap-webjarsは、webjar からの静的アセットを提供するためのリングミドルウェア。

wrap-formatは、リクエストとレスポンスの “accept"、"accept-charset “と"content-type" ヘッダに基づいてボディをネゴシエートし、Muuntaja のインスタンスが添付されたボディを :bodyparams にデコードするミドルウェア。

(ns duct.middleware.web
  (:require ;;略
            [muuntaja.middleware :as mm]                           ;;
            [ring.middleware.webjars :refer [wrap-webjars]]        ;;
            ))
;;略

(defmethod ig/init-key ::webjars [_ {:keys [path] :or {path "/assets"}}]     ;;
  #(wrap-webjars % path))

(defn- deep-merge [a b]
  (if (and (map? a) (map? b))
    (merge-with deep-merge a b)
    b))

(defmethod ig/init-key ::format [_ options]
  #(mm/wrap-format % (deep-merge mc/default-options options)))     ;;