Hunchentoot List of Redirects

2019-07-25 15:56发布

The URI structure of my website changed drastically recently and I need to redirect all of the old pages to their corresponding new pages. I have a dotted list of pairs of all of the old and new URIs. At the moment I am trying to define easy handlers for each in a loop:

(let ((redirects '(("/old/uri/example-1" . "/new/uri/example-1"))))
  (dolist (redirect redirects)
    (hunchentoot:define-easy-handler (???? :uri (first redirect)) ()
       (redirect (rest redirect)))
    ))

Maybe there is a better way. Assuming define-easy-handler is correct, it requires a function symbol for each easy handler. I tried the following to no avail:

  1. Placing a (gensym) where it expects a function symbol
  2. Using lists rather than dotted lists and calling (first redirect) where it expects a symbol
  3. Placing a quasiquote around the whole thing and an unquote around (first redirect)

What would be a good way to accomplish this?

3条回答
兄弟一词,经得起流年.
2楼-- · 2019-07-25 16:14

Although you already solved the problem I figured I might add this as an alternative. If you don't want to make a whole custom acceptor, you can add an around-method on HUNCHENTOOT:ACCEPTOR-DISPATCH-REQUEST for HUNCHENTOOT:EASY-HANDLER.

Let's make an acceptor and one page first:

(defparameter *acceptor* (make-instance 'hunchentoot:easy-acceptor :port 4242))

(hunchentoot:define-easy-handler (foo :uri "/foo") ()
  (format nil "<html><body><h1>Test</h1><p>foo</p></body></html>"))

(hunchentoot:start *acceptor*)

Then redirect /bar and /quux to /foo:

;; A simple helper to create prefix dispatchers.
(defun make-redirect-list (redirects)
  (mapcar (lambda (redirect)
            (destructuring-bind (from . to) redirect
              (hunchentoot:create-prefix-dispatcher from
                                                    (lambda ()
                                                      (hunchentoot:redirect to)))))
          redirects))

(defparameter *redirects* (make-redirect-list
                           '(("/bar" . "/foo")
                             ("/quux" . "/foo"))))

(defmethod hunchentoot:acceptor-dispatch-request :around
    ((acceptor hunchentoot:easy-acceptor) request)
  (dolist (redirect *redirects*)
    ;; Match the request against the prefix dispatchers in *REDIRECTS*...
    (let ((handler (funcall redirect request)))
      (when handler
        ;; and call the corresponding handler if a match is found.
        (return-from hunchentoot:acceptor-dispatch-request
          (funcall handler)))))
    ;; Unless a handler was found, call next method to 
    ;; handle the request normally.
  (call-next-method))

Edit: Use around method instead of before. I initially figured that letting it call the main method normally would be necessary for any logging/etc. happening there, but after further testing it doesn't seem to be.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-07-25 16:25

Let's guess: DEFINE-EASY-HANDLER is a macro.

Three typical ways to solve that:

  • call the underlying layer instead and don't use the macro - if the underlying layer is available for the programmer

  • write and use a macro which

expands (defredirects (a . a1) (b . b1) (c . c1))) into

(progn
  (hunchentoot:define-easy-handler (f-a ... a) () (... a1))
  (hunchentoot:define-easy-handler (f-b ... b) () (... b1))
  (hunchentoot:define-easy-handler (f-c ... c) () (... c1)))
  • Generate the form you want to call and use eval (or compile and funcall if possible) in the loop for each form.
查看更多
Anthone
4楼-- · 2019-07-25 16:26

This solution works. I definitely appreciate feedback regarding whether or not it's best practice.

(defun add-redirect (name from to)
  (eval `(hunchentoot:define-easy-handler (,name :uri ,from) ()
    (redirect ,to))))

(defun add-redirects (redirects)
  (dolist (redirect redirects)
    (add-redirect (first redirect) (second redirect) (third redirect))
    ))

(add-redirects
 '(
   (redirect-1 "/redirect-1/" "/destination-1/")
   (redirect-2 "/redirect-2/" "/destination-2/")
   (redirect-3 "/redirect-3/" "/destination-3/")
   ))
查看更多
登录 后发表回答