-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rkt
121 lines (96 loc) · 4.05 KB
/
main.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#lang racket
(require web-server/servlet
racket-route-match
response-ext)
(provide routy/get
routy/post
routy/delete
routy/put
routy/patch
routy/not-found
routy/files
request/param
routy/response)
(struct handler (route proc))
; Request handlers
(define handlers (make-hash '((get . ()) (post . ()) (put . ()) (delete . ()) (patch . ()))))
(define not-found-proc (λ (req) "Page not found"))
; Add request handler
(define/contract (routy/handler method path proc #:constraints constraints)
(symbol? string? (procedure-arity-includes/c 2) #:constraints (listof pair?) . -> . any/c) ;contract
(hash-set! handlers
method
(append (hash-ref handlers method) (list (handler (route-compile path constraints) proc)))))
; GET request handler
(define (routy/get path proc #:constraints [constraints '()])
(routy/handler 'get path proc #:constraints constraints))
; POST request handler
(define (routy/post path proc #:constraints [constraints '()])
(routy/handler 'post path proc #:constraints constraints))
; PUT request handler
(define (routy/put path proc #:constraints [constraints '()])
(routy/handler 'put path proc #:constraints constraints))
; DELETE request handler
(define (routy/delete path proc #:constraints [constraints '()])
(routy/handler 'delete path proc #:constraints constraints))
; PATCH request handler
(define (routy/patch path proc #:constraints [constraints '()])
(routy/handler 'patch path proc #:constraints constraints))
; Get request parameter
(define/contract (request/param params name)
(list? symbol? . -> . string?)
(cdr (assoc name params)))
; 404 handler
(define/contract (routy/not-found content)
((or/c string? procedure?) . -> . any/c) ;contract
(cond
[(string? content) (set! not-found-proc (λ (req) (content)))]
[(procedure? content) (set! not-found-proc content)]))
; files handler
(define/contract (routy/files path #:root [root (current-directory)])
((path-string?) (#:root path-string?) . ->* . any/c) ;contract
(routy/get (string-append (string-trim path "/") "/**") (λ (req params)
(let ([fullpath (build-path root (path->relative-path (simplify-path (url->path (request-uri req)))))])
(if (file-exists? fullpath)
(response/file fullpath)
(response/not-found-internal req))))))
; Makes response
(define/contract (routy/response req)
(request? . -> . response?) ; contract
(let ([handler-params (find-handler req)])
(case handler-params
[(#f) (response/not-found-internal req)]
[else
(let ([handler (first handler-params)]
[params (second handler-params)])
(let* ([proc (handler-proc handler)]
[route (handler-route handler)]
[resp (proc req params)])
(if (response? resp)
resp
(response/make resp))))])))
;; PRIVATE
(define (response/not-found-internal req)
(let ([content (not-found-proc req)])
(if (response? content)
content
(response/not-found content))))
; Find handler
(define (find-handler req)
(let* ([method
(case (request-method req)
[(#"GET") 'get]
[(#"POST") 'post]
[(#"PUT") 'put]
[(#"DELETE") 'delete]
[(#"PATCH") 'patch])]
[method-handlers
(hash-ref handlers method)])
(for/or ([handler method-handlers])
(let ([params (route-match (handler-route handler) (request-uri req))])
(if params (list handler params) #f)))))
; Convert path to relative path
(define (path->relative-path path) ;; path-string? -> path-string?
(if (absolute-path? path)
(apply build-path (cdr (explode-path path))) ;remove leading /
path))