【问题标题】:Hunchentoot List of RedirectsHunchentoot 重定向列表
【发布时间】:2016-04-10 19:05:00
【问题描述】:

我网站的 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. 在整个内容周围放置一个 quasiquote 并在周围放置一个 unquote(第一次重定向)

什么是完成此任务的好方法?

【问题讨论】:

  • 您可能需要考虑创建custom acceptor 而不是使用简单的处理程序。这样你就可以随心所欲地进行重定向。
  • 谢谢@jkiiski。您的建议似乎比我的解决方法更好。我渴望有朝一日获得足够的技能来实施这样的解决方案。

标签: common-lisp hunchentoot


【解决方案1】:

让我们猜一下: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(或compilefuncall)。

【讨论】:

  • 感谢您提供的可用选项列表。请参阅下面的解决方案。
【解决方案2】:

虽然你已经解决了这个问题,但我想我可能会添加这个作为替代方案。如果您不想制作一个完整的自定义接受器,您可以在 HUNCHENTOOT:ACCEPTOR-DISPATCH-REQUEST 上为 HUNCHENTOOT:EASY-HANDLER 添加一个环绕方法。

让我们先做一个acceptor和一个页面:

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

编辑:使用 around 方法而不是 before。我最初认为让它正常调用 main 方法对于任何日志记录/等都是必要的。那里发生了,但经过进一步测试后似乎没有。

【讨论】:

  • 太棒了。我一直遵循您的代码,直到 CLOS 部分。很明显,这个解决方案比我的要好。它使用 lambdas 来避免混淆命名空间,而不是为每个重定向创建单独的函数。它还避免使用 eval 和 quasiquote 来强制 Lisp 使用变量函数名。
  • @SpyroSoft 我将代码更改为使用环绕方法而不是前方法。 ACCEPTOR-DISPATCH-REQUEST 是 hunchentoot 接受者用来确定如何处理请求的方法。为它定义一个环绕方法基本上意味着每次调用该方法都会首先调用它。由于这是一个环绕方法,因此需要CALL-NEXT-METHOD 来调用由 hunchentoot 定义的实际 main-method(before-methods 会自动调用它)。当然,如果其中一个重定向匹配请求,RETURN-FROM 用于提前返回而不调用主方法。
【解决方案3】:

此解决方案有效。我非常感谢有关这是否是最佳做法的反馈。

(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/")
   ))

【讨论】:

  • 原来让我失望的是函数定义本身。当我尝试使用符号时,我收到错误“作为函数名不合法:”。我不确定如何生成函数符号?我什至不确定这是正确的术语。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
  • 2011-02-10
  • 2018-06-17
  • 2019-04-15
  • 1970-01-01
相关资源
最近更新 更多