2011-11-05 28 views
7

Tôi đang cố gắng viết một máy chủ ứng dụng bằng cách sử dụng Happstack, Heist và web-routes, nhưng gặp khó khăn trong việc tìm ra cách để cho các giá trị truy cập không bắt nguồn từ chồng đơn lẻ của ứng dụng của tôi.Sử dụng các giá trị không phải từ mẫu đơn ứng dụng với các mẫu Heist

Có hai tình huống nơi này đi lên:

  • thông số chiết xuất từ ​​đường dẫn URL qua web tìm đường khác. Chúng xuất phát từ mẫu khớp với URL an toàn loại khi định tuyến yêu cầu đến trình xử lý thích hợp.
  • Thông tin phiên. Nếu yêu cầu là phiên mới, tôi không thể đọc số nhận dạng phiên từ cookie trong yêu cầu (vì không có cookie như vậy) và tôi không thể sử dụng các kết nối để tạo phiên mới nếu cần, kể từ đó nếu nhiều hơn một mối nối cố gắng làm điều đó, tôi sẽ tạo ra nhiều phiên mới cho một yêu cầu duy nhất. Nhưng nếu tôi tạo phiên trước khi nhập nội dung trên web, phiên tồn tại bên ngoài đơn đăng ký.

Hãy xem xét các chương trình mẫu sau mà cố gắng để phục vụ các URL sau:

  • /thừa/n kết quả đầu ra giai thừa của n
  • /ngược/str đầu ra str backwards

Vì tham số xuất hiện trong đường dẫn URL thay vì chuỗi truy vấn, nó được trích xuất qua các tuyến đường web thay vì đến từ đơn nguyên ServerPartT. Mặc dù vậy, từ đó, không có cách nào rõ ràng để đặt tham số ở đâu đó mà các liên kết có thể nhìn thấy nó, vì chúng chỉ có quyền truy cập vào đơn ứng dụng.

Các giải pháp rõ ràng gắn bó một ReaderT ở đâu đó trên đơn nguyên stack có hai vấn đề:

  • Có ReaderT trên ServerPartT ẩn những phần Happstack của đơn nguyên đống, vì ReaderT không thực hiện ServerMonad, FilterMonad, v.v.
  • Giả định rằng tất cả các trang tôi đang phân phát đều có cùng loại tham số, nhưng trong ví dụ này,/giai thừa muốn Int nhưng ngược lại muốn có Chuỗi. Nhưng đối với cả hai trình xử lý trang để sử dụng cùng một TemplateDirectory, ReaderT sẽ cần phải mang theo một giá trị cùng loại.

Nhìn trộm tài liệu Snap, có vẻ như Snap các tham số xử lý trong đường dẫn URL bằng cách sao chép chúng hiệu quả vào chuỗi truy vấn, điều này sẽ tránh được vấn đề. Nhưng đó không phải là một tùy chọn với Happstack và các tuyến đường web, và bên cạnh đó, có hai cách khác nhau để một URL chỉ định cùng một giá trị đánh tôi như một ý tưởng tồi về bảo mật. Vì vậy, có cách "thích hợp" để hiển thị dữ liệu yêu cầu không áp dụng đơn lẻ cho các mối nối hay tôi cần phải từ bỏ Heist và sử dụng thứ gì đó như Blaze-HTML thay vì đây không phải là vấn đề? Tôi cảm thấy như tôi đang thiếu một cái gì đó hiển nhiên, nhưng không thể tìm ra nó có thể là gì.

Ví dụ mã:

{-# LANGUAGE TemplateHaskell #-} 

import Prelude hiding ((.)) 

import Control.Category ((.)) 
import Happstack.Server (Response, ServerPartT, nullConf, ok, simpleHTTP) 
import Happstack.Server.Heist (render) 
import Text.Boomerang.TH (derivePrinterParsers) 
import Text.Templating.Heist (Splice, bindSplices, emptyTemplateState, getParamNode) 
import Text.Templating.Heist.TemplateDirectory (TemplateDirectory, newTemplateDirectory') 
import Web.Routes (RouteT, Site, runRouteT) 
import Web.Routes.Boomerang (Router, anyString, boomerangSite, int, lit, (<>), (</>)) 
import Web.Routes.Happstack (implSite) 

import qualified Data.ByteString.Char8 as C 
import qualified Data.Text as T 
import qualified Text.XmlHtml as X 

data Sitemap = Factorial Int 
      | Reverse String 

$(derivePrinterParsers ''Sitemap) 

-- Conversion between type-safe URLs and URL strings. 
sitemap :: Router Sitemap 
sitemap = rFactorial . (lit "factorial" </> int) 
     <> rReverse . (lit "reverse" </> anyString) 

-- Serve a page for each type-safe URL. 
route :: TemplateDirectory (RouteT Sitemap (ServerPartT IO)) -> Sitemap -> RouteT Sitemap (ServerPartT IO) Response 
route templates url = case url of 
         Factorial _num -> render templates (C.pack "factorial") >>= ok 
         Reverse _str -> render templates (C.pack "reverse") >>= ok 

site :: TemplateDirectory (RouteT Sitemap (ServerPartT IO)) -> Site Sitemap (ServerPartT IO Response) 
site templates = boomerangSite (runRouteT $ route templates) sitemap 

-- <factorial>n</factorial> --> n! 
factorialSplice :: (Monad m) => Splice m 
factorialSplice = do input <- getParamNode 
        let n = read . T.unpack $ X.nodeText input :: Int 
        return [X.TextNode . T.pack . show $ product [1 .. n]] 

-- <reverse>text</reverse> --> reversed text 
reverseSplice :: (Monad m) => Splice m 
reverseSplice = do input <- getParamNode 
        return [X.TextNode . T.reverse $ X.nodeText input] 

main :: IO() 
main = do templates <- newTemplateDirectory' path . bindSplices splices $ emptyTemplateState path 
      simpleHTTP nullConf $ implSite "http://localhost:8000" "" $ site templates 
    where splices = [(T.pack "factorial", factorialSplice), (T.pack "reverse", reverseSplice)] 
      path = "." 

thừa.tpl:

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
     <meta charset="utf-8"/> 
     <title>Factorial</title> 
    </head> 
    <body> 
     <p>The factorial of 6 is <factorial>6</factorial>.</p> 
     <p>The factorial of ??? is ???.</p> 
    </body> 
</html> 

reverse.tpl:

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
     <meta charset="utf-8"/> 
     <title>Reverse</title> 
    </head> 
    <body> 
     <p>The reverse of "<tt>hello world</tt>" is "<tt><reverse>hello world</reverse></tt>".</p> 
     <p>The reverse of "<tt>???</tt>" is "<tt>???</tt>".</p> 
    </body> 
</html> 

Trả lời

4

Xem xét một chức năng với các hình thức sau đây:

func :: a -> m b 

Bởi vì Haskell là tinh khiết và có một hệ thống kiểu tĩnh mạnh mẽ, dữ liệu được sử dụng trong chức năng này chỉ có thể đến từ ba nơi: biểu tượng toàn cục nằm trong phạm vi hoặc được nhập, tham số ('a') và ngữ cảnh đơn nguyên 'm'. Vì vậy, vấn đề bạn mô tả không phải là duy nhất cho Heist, đó là một thực tế của việc sử dụng Haskell.

Điều này gợi ý một số cách giải quyết vấn đề của bạn. Một là truyền dữ liệu bạn cần làm đối số cho các hàm nối của bạn. Một cái gì đó như thế này:

factorialSplice :: Int -> TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node] 
factorialSplice n = return [X.TextNode . T.pack . show $ product [1 .. n]] 

Trong Snap chúng tôi có một chức năng gọi là renderWithSplices cho phép bạn liên kết một số chỗ nối ngay trước khi bạn làm cho mẫu. Bạn có thể sử dụng một hàm như thế này để liên kết mối nối bên phải trên dòng mà bạn hiện đang có "hiển thị mẫu".

Cách tiếp cận thứ hai là sử dụng đơn nguyên cơ bản. Bạn nói rằng "không có cách nào rõ ràng để đặt tham số ở đâu đó mà các liên kết có thể nhìn thấy nó, vì chúng chỉ có quyền truy cập vào đơn ứng dụng." Trong tâm trí của tôi, có quyền truy cập vào "đơn ứng dụng" là chính xác những gì bạn cần để có được công cụ này bên trong các mối nối. Vì vậy, đề nghị thứ hai của tôi là sử dụng nó. Nếu đơn ứng dụng bạn đang sử dụng không có dữ liệu đó, thì đó là sự thiếu hụt của đơn nguyên đó, chứ không phải vấn đề của Heist.

Như bạn có thể thấy trong loại chữ ký ở trên, TemplateMonad là một trình biến đổi đơn nguyên nơi đơn nguyên cơ bản là (Sơ đồ trang web RouteT (ServerPartT IO)). Điều này cho phép truy cập mối nối vào mọi thứ trong đơn nguyên cơ bản thông qua một thang máy đơn giản. Tôi chưa bao giờ sử dụng các tuyến đường web, nhưng có vẻ như với tôi rằng phải có một chức năng RouteT để có được tại Sơ đồ trang web đó. Giả sử hàm sau tồn tại:

getUrlData :: RouteT url m url 

Sau đó, bạn sẽ có thể viết:

factorialSplice :: TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node] 
factorialSplice = do 
    url <- lift getUrlData 
    return $ case url of 
     Factorial n -> [X.TextNode . T.pack . show $ product [1 .. n]] 
     _ -> [] 

Hoặc để khái quát nó một chút, bạn có thể làm điều này:

factorialArgSplice :: TemplateMonad (RouteT Sitemap (ServerPartT IO)) [X.Node] 
factorialArgSplice = do 
    url <- lift getUrlData 
    return $ case url of 
     Factorial n -> [X.TextNode . T.pack . show $ n] 
     _ -> [] 

Sau đó, bạn có thể liên kết thẻ đó với thẻ <factorialArg> và thực hiện các thao tác sau trong mẫu của bạn.

<p>The factorial of <factorialArg> is <factorial><factorialArg/></factorial>.</p> 
+0

Tôi đã sử dụng biến thể tùy chỉnh happstack trên snap 'rendWithSplices' ở đây: https://github.com/aslatter/blog/blob/master/Blog/Templates.hs Trong ví dụ này, 'appTemplates 'chức năng nếu loại' App TemplateState '. –

Các vấn đề liên quan