Список редиректов Hunchentoot

Структура URI моего веб-сайта недавно резко изменилась, и мне нужно перенаправить все старые страницы на соответствующие им новые страницы. У меня есть точечный список пар всех старых и новых URI. На данный момент я пытаюсь определить простые обработчики для каждого в цикле:

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

Может быть, есть лучший способ. Предполагая, что define-easy-handler верен, для каждого простого обработчика требуется функциональный символ. Я пробовал следующее безрезультатно:

  1. Размещение (gensym) там, где ожидается функциональный символ
  2. Использование списков, а не точечных списков и вызов (первое перенаправление) там, где ожидается символ
  3. Размещение квазикавычек вокруг всего этого и отсутствие кавычек вокруг (первое перенаправление)

Что было бы хорошим способом сделать это?


person SpyroSoft    schedule 10.04.2016    source источник
comment
Возможно, вы захотите создать собственный акцептор вместо использования простых обработчиков. Таким образом, вы можете делать перенаправления так, как хотите.   -  person jkiiski    schedule 11.04.2016
comment
Спасибо @jkiiski. Ваше предложение кажется лучшим способом решить эту проблему, чем я. Я стремлюсь получить достаточно навыков, чтобы когда-нибудь реализовать такое решение.   -  person SpyroSoft    schedule 12.04.2016


Ответы (3)


Предположим: DEFINE-EASY-HANDLER — это макрос.

Три типичных способа решить эту проблему:

  • вместо этого вызовите базовый слой и не используйте макрос - если базовый слой доступен для программиста

  • написать и использовать макрос, который

расширяется (defredirects (a . a1) (b . b1) (c . c1))) в

(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)))
  • Создайте форму, которую вы хотите вызвать, и используйте eval (или compile и funcall, если возможно) в цикле для каждой формы.
person Rainer Joswig    schedule 10.04.2016
comment
Спасибо за список доступных вариантов. Смотрите мое решение ниже. - person SpyroSoft; 12.04.2016

Хотя вы уже решили проблему, я решил добавить это в качестве альтернативы. Если вы не хотите создавать полностью собственный акцептор, вы можете добавить обходной метод на HUNCHENTOOT:ACCEPTOR-DISPATCH-REQUEST для HUNCHENTOOT:EASY-HANDLER.

Давайте сначала создадим акцептор и одну страницу:

(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*)

Затем перенаправьте /bar и /quux на /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))

Редактировать: используйте метод вокруг, а не раньше. Первоначально я полагал, что для любого ведения журнала и т. д. обычно требуется вызов основного метода. происходит там, но после дальнейшего тестирования кажется, что это не так.

person jkiiski    schedule 12.04.2016
comment
Потрясающий. Я следую вашему коду до части CLOS. Очевидно, что это решение лучше моего. Он использует лямбда-выражения, чтобы не загромождать пространство имен, а не создавать отдельные функции для каждого перенаправления. Он также избегает использования eval и квазицитирования для принуждения Lisp к использованию имен переменных функций. - person SpyroSoft; 12.04.2016
comment
@SpyroSoft Я изменил код, чтобы использовать метод вокруг вместо метода до. ACCEPTOR-DISPATCH-REQUEST — это метод, который используют приемщики hunchentoot, чтобы выяснить, что делать с запросами. Определение метода обхода для него в основном означает, что этот метод будет вызываться первым каждый раз, когда он вызывается. Поскольку это обходной метод, CALL-NEXT-METHOD необходимо для вызова фактического основного метода, определенного в hunchentoot (ранее-методы вызывали его автоматически). Конечно, если одно из перенаправлений соответствует запросу, RETURN-FROM используется для раннего возврата без вызова основного метода. - person jkiiski; 13.04.2016

Это решение работает. Я определенно ценю отзывы о том, является ли это лучшей практикой.

(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/")
   ))
person SpyroSoft    schedule 12.04.2016
comment
Оказывается, меня сбивало с толку само определение функции. Я получал сообщение об ошибке Недопустимо как имя функции: когда я пытался использовать символ. Я не уверен, как создать функциональный символ? Я даже не уверен, что это правильная терминология. - person SpyroSoft; 12.04.2016