Changed to new redirect system (#202)
This commit is contained in:
parent
938f70af51
commit
95b6678e9f
@ -39,11 +39,9 @@ module Yesod.Handler
|
|||||||
, runRequestBody
|
, runRequestBody
|
||||||
-- * Special responses
|
-- * Special responses
|
||||||
-- ** Redirecting
|
-- ** Redirecting
|
||||||
, RedirectType (..)
|
, RedirectUrl (..)
|
||||||
, redirect
|
, redirect
|
||||||
, redirectParams
|
, redirectWith
|
||||||
, redirectString
|
|
||||||
, redirectText
|
|
||||||
, redirectToPost
|
, redirectToPost
|
||||||
-- ** Errors
|
-- ** Errors
|
||||||
, notFound
|
, notFound
|
||||||
@ -77,8 +75,6 @@ module Yesod.Handler
|
|||||||
, deleteSession
|
, deleteSession
|
||||||
-- ** Ultimate destination
|
-- ** Ultimate destination
|
||||||
, setUltDest
|
, setUltDest
|
||||||
, setUltDestString
|
|
||||||
, setUltDestText
|
|
||||||
, setUltDest'
|
, setUltDest'
|
||||||
, setUltDestReferer
|
, setUltDestReferer
|
||||||
, redirectUltDest
|
, redirectUltDest
|
||||||
@ -298,7 +294,7 @@ data HandlerContents =
|
|||||||
HCContent H.Status ChooseRep
|
HCContent H.Status ChooseRep
|
||||||
| HCError ErrorResponse
|
| HCError ErrorResponse
|
||||||
| HCSendFile ContentType FilePath (Maybe W.FilePart) -- FIXME replace FilePath with opaque type from system-filepath?
|
| HCSendFile ContentType FilePath (Maybe W.FilePart) -- FIXME replace FilePath with opaque type from system-filepath?
|
||||||
| HCRedirect RedirectType Text
|
| HCRedirect H.Status Text
|
||||||
| HCCreated Text
|
| HCCreated Text
|
||||||
| HCWai W.Response
|
| HCWai W.Response
|
||||||
deriving Typeable
|
deriving Typeable
|
||||||
@ -416,10 +412,10 @@ runHandler handler mrender sroute tomr master sub =
|
|||||||
(ct, c) <- liftIO $ a cts
|
(ct, c) <- liftIO $ a cts
|
||||||
return $ YARPlain status (appEndo headers []) ct c finalSession
|
return $ YARPlain status (appEndo headers []) ct c finalSession
|
||||||
HCError e -> handleError e
|
HCError e -> handleError e
|
||||||
HCRedirect rt loc -> do
|
HCRedirect status loc -> do
|
||||||
let hs = Header "Location" (encodeUtf8 loc) : appEndo headers []
|
let hs = Header "Location" (encodeUtf8 loc) : appEndo headers []
|
||||||
return $ YARPlain
|
return $ YARPlain
|
||||||
(getRedirectStatus rt $ reqWaiRequest rr) hs typePlain emptyContent
|
status hs typePlain emptyContent
|
||||||
finalSession
|
finalSession
|
||||||
HCSendFile ct fp p -> catch
|
HCSendFile ct fp p -> catch
|
||||||
(sendFile' ct fp p)
|
(sendFile' ct fp p)
|
||||||
@ -444,22 +440,28 @@ safeEh er = YesodApp $ \_ _ _ session -> do
|
|||||||
(toContent ("Internal Server Error" :: S.ByteString))
|
(toContent ("Internal Server Error" :: S.ByteString))
|
||||||
session
|
session
|
||||||
|
|
||||||
-- | Redirect to the given route.
|
-- | Redirect to the given route. The redirect will be a temporary redirect to
|
||||||
redirect :: RedirectType -> Route master -> GHandler sub master a
|
-- a GET request. This is the appropriate choice for a get-following-post
|
||||||
redirect rt url = redirectParams rt url []
|
-- technique, which should be the usual use case. This function currently uses
|
||||||
|
-- a 302 status code, though the implementation is free to change in the future
|
||||||
|
-- to an equivalent code with the same semantics (e.g., 303).
|
||||||
|
--
|
||||||
|
-- If you want direct control of the final status code, or need a different
|
||||||
|
-- status code, please use 'redirectWith'.
|
||||||
|
--
|
||||||
|
-- Note: According to the HTTP spec, a 302 does /not/ have the semantics
|
||||||
|
-- described here. However, this has been historically how clients treated a
|
||||||
|
-- 302. 303 is not understood by older clients, which is why we have opted for
|
||||||
|
-- a 302. If at some future date it is determined that virtually all clients
|
||||||
|
-- understand 303, this implementation will switch to that status code.
|
||||||
|
redirect :: RedirectUrl master url => url -> GHandler sub master a
|
||||||
|
redirect = redirectWith H.status302
|
||||||
|
|
||||||
-- | Redirects to the given route with the associated query-string parameters.
|
-- | Redirect to the given URL with the specified status code.
|
||||||
redirectParams :: RedirectType -> Route master -> [(Text, Text)]
|
redirectWith :: RedirectUrl master url => H.Status -> url -> GHandler sub master a
|
||||||
-> GHandler sub master a
|
redirectWith status url = do
|
||||||
redirectParams rt url params = do
|
urlText <- toTextUrl url
|
||||||
r <- getUrlRenderParams
|
liftIO $ throwIO $ HCRedirect status urlText
|
||||||
redirectString rt $ r url params
|
|
||||||
|
|
||||||
-- | Redirect to the given URL.
|
|
||||||
redirectString, redirectText :: RedirectType -> Text -> GHandler sub master a
|
|
||||||
redirectText rt = liftIO . throwIO . HCRedirect rt
|
|
||||||
redirectString = redirectText
|
|
||||||
{-# DEPRECATED redirectString "Use redirectText instead" #-}
|
|
||||||
|
|
||||||
ultDestKey :: Text
|
ultDestKey :: Text
|
||||||
ultDestKey = "_ULT"
|
ultDestKey = "_ULT"
|
||||||
@ -468,18 +470,10 @@ ultDestKey = "_ULT"
|
|||||||
--
|
--
|
||||||
-- An ultimate destination is stored in the user session and can be loaded
|
-- An ultimate destination is stored in the user session and can be loaded
|
||||||
-- later by 'redirectUltDest'.
|
-- later by 'redirectUltDest'.
|
||||||
setUltDest :: Route master -> GHandler sub master ()
|
setUltDest :: RedirectUrl master url => url -> GHandler sub master ()
|
||||||
setUltDest dest = do
|
setUltDest url = do
|
||||||
render <- getUrlRender
|
urlText <- toTextUrl url
|
||||||
setUltDestString $ render dest
|
setSession ultDestKey urlText
|
||||||
|
|
||||||
-- | Same as 'setUltDest', but use the given string.
|
|
||||||
setUltDestText :: Text -> GHandler sub master ()
|
|
||||||
setUltDestText = setSession ultDestKey
|
|
||||||
|
|
||||||
setUltDestString :: Text -> GHandler sub master ()
|
|
||||||
setUltDestString = setSession ultDestKey
|
|
||||||
{-# DEPRECATED setUltDestString "Use setUltDestText instead" #-}
|
|
||||||
|
|
||||||
-- | Same as 'setUltDest', but uses the current page.
|
-- | Same as 'setUltDest', but uses the current page.
|
||||||
--
|
--
|
||||||
@ -493,8 +487,7 @@ setUltDest' = do
|
|||||||
Just r -> do
|
Just r -> do
|
||||||
tm <- getRouteToMaster
|
tm <- getRouteToMaster
|
||||||
gets' <- reqGetParams `liftM` handlerRequest `liftM` ask
|
gets' <- reqGetParams `liftM` handlerRequest `liftM` ask
|
||||||
render <- getUrlRenderParams
|
setUltDest (tm r, gets')
|
||||||
setUltDestString $ render (tm r) gets'
|
|
||||||
|
|
||||||
-- | Sets the ultimate destination to the referer request header, if present.
|
-- | Sets the ultimate destination to the referer request header, if present.
|
||||||
--
|
--
|
||||||
@ -507,19 +500,22 @@ setUltDestReferer = do
|
|||||||
(const $ return ())
|
(const $ return ())
|
||||||
mdest
|
mdest
|
||||||
where
|
where
|
||||||
setUltDestBS = setUltDestText . T.pack . S8.unpack
|
setUltDestBS = setUltDest . T.pack . S8.unpack
|
||||||
|
|
||||||
-- | Redirect to the ultimate destination in the user's session. Clear the
|
-- | Redirect to the ultimate destination in the user's session. Clear the
|
||||||
-- value from the session.
|
-- value from the session.
|
||||||
--
|
--
|
||||||
-- The ultimate destination is set with 'setUltDest'.
|
-- The ultimate destination is set with 'setUltDest'.
|
||||||
redirectUltDest :: RedirectType
|
--
|
||||||
-> Route master -- ^ default destination if nothing in session
|
-- This function uses 'redirect', and thus will perform a temporary redirect to
|
||||||
|
-- a GET request.
|
||||||
|
redirectUltDest :: RedirectUrl master url
|
||||||
|
=> url -- ^ default destination if nothing in session
|
||||||
-> GHandler sub master a
|
-> GHandler sub master a
|
||||||
redirectUltDest rt def = do
|
redirectUltDest def = do
|
||||||
mdest <- lookupSession ultDestKey
|
mdest <- lookupSession ultDestKey
|
||||||
deleteSession ultDestKey
|
deleteSession ultDestKey
|
||||||
maybe (redirect rt def) (redirectText rt) mdest
|
maybe (redirect def) redirect mdest
|
||||||
|
|
||||||
-- | Remove a previously set ultimate destination. See 'setUltDest'.
|
-- | Remove a previously set ultimate destination. See 'setUltDest'.
|
||||||
clearUltDest :: GHandler sub master ()
|
clearUltDest :: GHandler sub master ()
|
||||||
@ -703,18 +699,26 @@ getStatus (InvalidArgs _) = H.status400
|
|||||||
getStatus (PermissionDenied _) = H.status403
|
getStatus (PermissionDenied _) = H.status403
|
||||||
getStatus (BadMethod _) = H.status405
|
getStatus (BadMethod _) = H.status405
|
||||||
|
|
||||||
getRedirectStatus :: RedirectType -> W.Request -> H.Status
|
-- | Some value which can be turned into a URL for redirects.
|
||||||
getRedirectStatus RedirectPermanent _ = H.status301
|
class RedirectUrl master a where
|
||||||
getRedirectStatus RedirectTemporary r
|
-- | Converts the value to the URL and a list of query-string parameters.
|
||||||
| W.httpVersion r == H.http11 = H.status307
|
toTextUrl :: a -> GHandler sub master Text
|
||||||
| otherwise = H.status302
|
|
||||||
getRedirectStatus RedirectSeeOther _ = H.status303
|
|
||||||
|
|
||||||
-- | Different types of redirects.
|
instance RedirectUrl master Text where
|
||||||
data RedirectType = RedirectPermanent
|
toTextUrl = return
|
||||||
| RedirectTemporary
|
|
||||||
| RedirectSeeOther
|
instance RedirectUrl master String where
|
||||||
deriving (Show, Eq)
|
toTextUrl = toTextUrl . T.pack
|
||||||
|
|
||||||
|
instance RedirectUrl master (Route master) where
|
||||||
|
toTextUrl u = do
|
||||||
|
r <- getUrlRender
|
||||||
|
return $ r u
|
||||||
|
|
||||||
|
instance t ~ Text => RedirectUrl master (Route master, [(t, t)]) where
|
||||||
|
toTextUrl (u, ps) = do
|
||||||
|
r <- getUrlRenderParams
|
||||||
|
return $ r u ps
|
||||||
|
|
||||||
localNoCurrent :: GHandler s m a -> GHandler s m a
|
localNoCurrent :: GHandler s m a -> GHandler s m a
|
||||||
localNoCurrent =
|
localNoCurrent =
|
||||||
@ -835,8 +839,10 @@ newIdent = do
|
|||||||
-- POST form, and some Javascript to automatically submit the form. This can be
|
-- POST form, and some Javascript to automatically submit the form. This can be
|
||||||
-- useful when you need to post a plain link somewhere that needs to cause
|
-- useful when you need to post a plain link somewhere that needs to cause
|
||||||
-- changes on the server.
|
-- changes on the server.
|
||||||
redirectToPost :: Route master -> GHandler sub master a
|
redirectToPost :: RedirectUrl master url => url -> GHandler sub master a
|
||||||
redirectToPost dest = hamletToRepHtml
|
redirectToPost url = do
|
||||||
|
urlText <- toTextUrl url
|
||||||
|
hamletToRepHtml
|
||||||
#if GHC7
|
#if GHC7
|
||||||
[hamlet|
|
[hamlet|
|
||||||
#else
|
#else
|
||||||
@ -848,7 +854,7 @@ redirectToPost dest = hamletToRepHtml
|
|||||||
<head>
|
<head>
|
||||||
<title>Redirecting...
|
<title>Redirecting...
|
||||||
<body onload="document.getElementById('form').submit()">
|
<body onload="document.getElementById('form').submit()">
|
||||||
<form id="form" method="post" action="@{dest}">
|
<form id="form" method="post" action=#{urlText}>
|
||||||
<noscript>
|
<noscript>
|
||||||
<p>Javascript has been disabled; please click on the button below to be redirected.
|
<p>Javascript has been disabled; please click on the button below to be redirected.
|
||||||
<input type="submit" value="Continue">
|
<input type="submit" value="Continue">
|
||||||
|
|||||||
@ -375,7 +375,7 @@ defaultYesodRunner handler master sub murl toMasterRoute mkey req = do
|
|||||||
permissionDenied "Authentication required"
|
permissionDenied "Authentication required"
|
||||||
Just url' -> do
|
Just url' -> do
|
||||||
setUltDest'
|
setUltDest'
|
||||||
redirect RedirectTemporary url'
|
redirect url'
|
||||||
Unauthorized s' -> permissionDenied s'
|
Unauthorized s' -> permissionDenied s'
|
||||||
handler
|
handler
|
||||||
let sessionMap = Map.fromList
|
let sessionMap = Map.fromList
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import Test.Hspec.HUnit ()
|
|||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core hiding (Request)
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
|
import Network.HTTP.Types (status301)
|
||||||
|
|
||||||
data Y = Y
|
data Y = Y
|
||||||
mkYesod "Y" [parseRoutes|
|
mkYesod "Y" [parseRoutes|
|
||||||
@ -27,7 +28,7 @@ getRootR = error "FOOBAR" >> return ()
|
|||||||
getRedirR :: Handler ()
|
getRedirR :: Handler ()
|
||||||
getRedirR = do
|
getRedirR = do
|
||||||
setHeader "foo" "bar"
|
setHeader "foo" "bar"
|
||||||
redirect RedirectPermanent RootR
|
redirectWith status301 RootR
|
||||||
|
|
||||||
exceptionsTest :: [Spec]
|
exceptionsTest :: [Spec]
|
||||||
exceptionsTest = describe "Test.Exceptions"
|
exceptionsTest = describe "Test.Exceptions"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
module YesodCoreTest.Redirect (specs, Widget) where
|
module YesodCoreTest.Redirect (specs, Widget) where
|
||||||
|
|
||||||
import YesodCoreTest.YesodTest
|
import YesodCoreTest.YesodTest
|
||||||
import Yesod.Handler (RedirectType(..))
|
import Yesod.Handler (redirectWith)
|
||||||
import qualified Network.HTTP.Types as H
|
import qualified Network.HTTP.Types as H
|
||||||
|
|
||||||
data Y = Y
|
data Y = Y
|
||||||
@ -11,6 +11,7 @@ mkYesod "Y" [parseRoutes|
|
|||||||
/r301 R301 GET
|
/r301 R301 GET
|
||||||
/r303 R303 GET
|
/r303 R303 GET
|
||||||
/r307 R307 GET
|
/r307 R307 GET
|
||||||
|
/rregular RRegular GET
|
||||||
|]
|
|]
|
||||||
instance Yesod Y where approot _ = "http://test"
|
instance Yesod Y where approot _ = "http://test"
|
||||||
app :: Session () -> IO ()
|
app :: Session () -> IO ()
|
||||||
@ -19,11 +20,11 @@ app = yesod Y
|
|||||||
getRootR :: Handler ()
|
getRootR :: Handler ()
|
||||||
getRootR = return ()
|
getRootR = return ()
|
||||||
|
|
||||||
getR301, getR303, getR307 :: Handler ()
|
getR301, getR303, getR307, getRRegular :: Handler ()
|
||||||
getR301 = redirect RedirectPermanent RootR
|
getR301 = redirectWith H.status301 RootR
|
||||||
getR303 = redirect RedirectSeeOther RootR
|
getR303 = redirectWith H.status303 RootR
|
||||||
getR307 = redirect RedirectTemporary RootR
|
getR307 = redirectWith H.status307 RootR
|
||||||
|
getRRegular = redirect RootR
|
||||||
|
|
||||||
specs :: [Spec]
|
specs :: [Spec]
|
||||||
specs = describe "Redirect" [
|
specs = describe "Redirect" [
|
||||||
@ -42,9 +43,9 @@ specs = describe "Redirect" [
|
|||||||
assertStatus 307 res
|
assertStatus 307 res
|
||||||
assertBodyContains "" res
|
assertBodyContains "" res
|
||||||
|
|
||||||
, it "302 redirect instead of 307 for http 1.0" $ app $ do
|
, it "302 redirect for regular" $ app $ do
|
||||||
res <- request defaultRequest {
|
res <- request defaultRequest {
|
||||||
pathInfo = ["r307"], httpVersion = H.http10
|
pathInfo = ["rregular"]
|
||||||
}
|
}
|
||||||
assertStatus 302 res
|
assertStatus 302 res
|
||||||
assertBodyContains "" res
|
assertBodyContains "" res
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user