Add input validation

Input validation uses Malli coercion. Sadly enough, email is validated
with a regexp.
This commit is contained in:
2026-01-31 00:15:01 +01:00
parent b9ae9a8b56
commit da6f8b4519
3 changed files with 32 additions and 5 deletions

View File

@@ -27,3 +27,5 @@ The following links proved more than useful when working on this assignment:
* <https://practical.li/clojure-web-services/building-api/> * <https://practical.li/clojure-web-services/building-api/>
* <https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md> * <https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md>
* <https://ostash.dev/posts/2021-08-22-data-validation-in-clojure/>
* <https://clojurecivitas.github.io/malli/elements_of_malli.html>

View File

@@ -7,6 +7,7 @@
[ring/ring-core "1.15.3"] [ring/ring-core "1.15.3"]
[ring/ring-jetty-adapter "1.15.3"] [ring/ring-jetty-adapter "1.15.3"]
[metosin/reitit "0.10.0"] [metosin/reitit "0.10.0"]
[metosin/reitit-malli "0.10.0"]
[metosin/muuntaja "0.6.11"]] [metosin/muuntaja "0.6.11"]]
:main ^:skip-aot yohoho.core :main ^:skip-aot yohoho.core
:target-path "target/%s" :target-path "target/%s"

View File

@@ -2,10 +2,26 @@
(:require [reitit.ring :as ring] (:require [reitit.ring :as ring]
[ring.adapter.jetty :as http-server] [ring.adapter.jetty :as http-server]
[reitit.ring.middleware.muuntaja :as muuntaja] [reitit.ring.middleware.muuntaja :as muuntaja]
[muuntaja.core :as m]) [muuntaja.core :as m]
[reitit.coercion.malli :as malli]
[reitit.ring.coercion :as coercion])
(:gen-class)) (:gen-class))
;; Data schema ----------------------------------------------------
;; Malli schemas for validation
;; Validating an email address is hard.
;; Regexp shamelessly taken from https://emailregex.com/
(def email-regexp #"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])")
(def Email [:re email-regexp])
(def Item
[:map
[:name :string]
[:email Email]])
;; Handlers ------------------------------------------------------- ;; Handlers -------------------------------------------------------
(defn ahoy-handler (defn ahoy-handler
@@ -52,11 +68,19 @@
(ring/router (ring/router
[["/ahoy" {:get ahoy-handler}] [["/ahoy" {:get ahoy-handler}]
["/items" {:get get-items-handler ["/items" {:get get-items-handler
:post post-items-handler}]] :post {:handler post-items-handler
;; Use muuntaja middleware to automatically decode JSON :parameters {:body Item}}}]]
{:data {:muuntaja m/instance ;; Middlewares:
;; - wrap-content-type-json: ensure POST routes are sent JSON payload
;; - muuntaja middleware to automatically decode JSON
;; - malli + coercion: handle data validation
{:data {:coercion malli/coercion
:muuntaja m/instance
:middleware [wrap-content-type-json :middleware [wrap-content-type-json
muuntaja/format-middleware]}}) muuntaja/format-middleware
coercion/coerce-exceptions-middleware
coercion/coerce-request-middleware
coercion/coerce-response-middleware]}})
;; Default route: anything not explicitely handled should give a 404 ;; Default route: anything not explicitely handled should give a 404
(ring/create-default-handler (ring/create-default-handler
{:not-found (constantly {:status 404 {:not-found (constantly {:status 404