HTML из БД не внедряется в маршрут hunchentoot

В моем приложении есть много маршрутов, которые получают информацию из базы данных. В одной конкретной ситуации я получаю HTML-контент из базы данных для его рендеринга с помощьюcl-who.

Я не уверен, почему контент из БД не отображается. Остальная часть страницы работает нормально.

Когда я тестирую добавление html в маршрут вручную (жестко закодированные html-теги), все работает нормально. В случае, когда я использую(second (get-content))(см. ниже) HTML из БД в шаблон не добавляется.

Что мне не хватает?

Макрос базы данных

      (defmacro access-db(db &body query)
  `(progn
     (clsql:connect ,db)
     (unwind-protect (progn ,@query)
      (disconnect-db ,db)))) 

Макрос страницы

      (defmacro defpage ((&key title) &body content)
  `(cl-who:with-html-output-to-string 
       (*standard-output* nil :prologue t :indent t)
     (:html
      :xmlns "http://www.w3.org/1999/xhtml"
      :xml\:lang "en"
      :lang "en"
      (:head
       (:meta
    :http-equiv "Content-Type"
    :content "text/html;charset=utf-8")
       (:title
    ,(format nil "~A" title))
       (:link :type "text/css" 
          :rel "stylesheet"
          :href "/styles.css"))
      (:body :class "whole-page" 
         (:div :class "site-articles"
           (:div :class "article-body"
             (:div :class "on-page-title"
                   (:h1 :class "main-header" ,title))
             ,@content))))))

Маршрут Hunchentoot

      (define-easy-handler (test-page :uri "/wow") ()
  (let ((content
      (bt:make-thread
       (lambda ()
         (second (get-content))))))
    (defpage (:title "Main One") 
      (bt:join-thread content))))

использование get-contentaccess-dbфункция и возвращает список первого элемента из базы данных. Заголовок и HTML. HTML занимает второе место в этом списке.

Получить контент

      (defun get-content ()
  (let ((blogs
      (access-db *PSQL-CONNECT-INFO*
        (clsql:query
         "select * from content"))))
    (first blogs)))

1 ответ

[Отказ от ответственности: я не могу заставить CLSQL работать и в любом случае не хочу иметь ничего общего с базами данных, поэтому этот ответ посвящен проблемам с другими вещами.]

Я думаю, что вы, возможно, запутались в макросах в целом, но ваш макрос определенно запутался.

Что такое CL-WHOwith-html-outputзаключается в том, чтобы привязать поток к переменной, в которую вы можете напечатать , а затем рассматривать его тело как неявный язык «html-lisp», который понимает он и все подобные макросы (я думаю, HTOUT был первым, кто действительно это сделал). Это означает, что если вы хотите отправить выходные данные в этот поток, вам необходимо напечатать их в этом потоке.with-html-output-to-stringпросто записывает этот вывод в строку очевидным образом.

Вот очищенная версия этого макроса с лучшим именем (defpageдля меня пахнет как «определить страницу», что совсем не то, что он делает).

      (defmacro with-output-to-standard-page ((&key (title "page")
                                              (stream '*standard-output*)
                                              (string-form 'nil))
                                        &body content)
  (let ((tv (make-symbol "TITLE")))
    `(let ((,tv ,title))
       (with-html-output-to-string (,var ,string-form :prologue t :indent t)
         (:html
          :xmlns "http://www.w3.org/1999/xhtml"
          :xml\:lang "en"
          :lang "en"
          (:head
           (:meta
            :http-equiv "Content-Type"
            :content "text/html;charset=utf-8")
           (:title (princ ,tv ,var))
           (:link :type "text/css" 
            :rel "stylesheet"
            :href "/styles.css"))
          (:body :class "whole-page" 
           (:div :class "site-articles"
            (:div :class "article-body"
             (:div :class "on-page-title"
              (:h1 :class "main-header"
               (princ ,tv ,var)))
             ,@content))))))))

Это поведение более традиционное, чем ваше:

      (let ((title "foo"))
  (with-output-to-standard-page (:title title)
    ...))

будет работать, и

      (with-output-to-standard-page (:title (complicated-function-with-state))
  ...)

позвонюcomplicated-function-with-stateтолько раз.

Кроме того, он позволяет вам определить, что такое переменная потока и строковая форма, если хотите:

      (with-output-to-standard-page (:var s)
  (princ "foo" s))

будет работать.

Наконец, он помещает контент под начальный, а не в него (и за пределы начального).on-page-title divна самом деле), и использует название дляh1.

Все это также делается в пакете, который используетCL-WHO,HUNCHENTOOTиBORDEAUX-THREADSчтобы избежать ужасного ужаса вездесущих префиксов пакетов и сделать код читабельным.

Итак, если мы заменим вашget-contentфункция с помощью прокладки, потому что нет SQL:

      (defun get-content ()
  (list 'nothing "my page content"))

Затем исправьте ваш обработчик для печати в потоке, что является критической проблемой.

      (define-easy-handler (test-page :uri "/wow") ()
  (let ((content-thread
         (make-thread
          (lambda ()
            (second (get-content))))))
    (with-output-to-standard-page (:var p :title "Main One") 
      (princ (join-thread content-thread) p))))

Теперь все будет работать.


Глядя на твойaccess-dbмакроса с ним тоже, почти наверняка, проблемы.

Прежде всего, я назову это примерно такwith-db-accessпотому чтоwith-*— это одно из общепринятых названий подобных макросов (другой вариант может бытьaccessing-db). Тогда у него, по крайней мере, есть проблема: он будет многократно оценивать свой первый аргумент. Вместо этого это должно быть что-то вроде

      (defmacro with-db-access (db &body query)
  (let ((dbv (make-symbol "DB")))
    `(let ((,dbv ,db))
       (connect ,dbv)
       (unwind-protect
           (progn ,@query)
         (disconnect-db ,dbv)))))

чтобы избежать этой проблемы.

Обратите внимание, что этот очень распространенный шаблон в макросах (и соответствующую очень распространенную проблему, когда люди об этом забывают) можно сделать намного проще, используя метатронные макросы Тима Брэдшоу . Используя это, это будет:

      (defmacro/m with-db-access (db &body query)
  `(let ((<db> ,db))
     (connect <db>)
     (unwind-protect
         (progn ,@query)
       (disconnect-db <db>))))

Мойwith-output-to-standard-pageмакрос, приведенный выше, будет аналогичным образом упрощен с использованием макросов Metatronic.

Другие вопросы по тегам