scotty-0.5.0: Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp

Safe HaskellNone

Web.Scotty

Contents

Description

It should be noted that most of the code snippets below depend on the OverloadedStrings language pragma.

Synopsis

scotty-to-WAI

scotty :: Port -> ScottyM () -> IO ()

Run a scotty application using the warp server.

scottyApp :: ScottyM () -> IO Application

Turn a scotty application into a WAI Application, which can be run with any WAI handler.

scottyOpts :: Options -> ScottyM () -> IO ()

Run a scotty application using the warp server, passing extra options.

data Options

Constructors

Options 

Fields

verbose :: Int

0 = silent, 1(def) = startup banner

settings :: Settings

Warp Settings

Instances

Defining Middleware and Routes

Middleware and routes are run in the order in which they are defined. All middleware is run first, followed by the first route that matches. If no route matches, a 404 response is given.

middleware :: Middleware -> ScottyM ()

Use given middleware. Middleware is nested such that the first declared is the outermost middleware (it has first dibs on the request and last action on the response). Every middleware is run on each request.

get :: RoutePattern -> ActionM () -> ScottyM ()

get = addroute GET

post :: RoutePattern -> ActionM () -> ScottyM ()

post = addroute POST

put :: RoutePattern -> ActionM () -> ScottyM ()

put = addroute PUT

delete :: RoutePattern -> ActionM () -> ScottyM ()

delete = addroute DELETE

patch :: RoutePattern -> ActionM () -> ScottyM ()

patch = addroute PATCH

addroute :: StdMethod -> RoutePattern -> ActionM () -> ScottyM ()

Define a route with a StdMethod, Text value representing the path spec, and a body (Action) which modifies the response.

 addroute GET "/" $ text "beam me up!"

The path spec can include values starting with a colon, which are interpreted as captures. These are named wildcards that can be looked up with param.

 addroute GET "/foo/:bar" $ do
     v <- param "bar"
     text v
>>> curl http://localhost:3000/foo/something
something

matchAny :: RoutePattern -> ActionM () -> ScottyM ()

Add a route that matches regardless of the HTTP verb.

notFound :: ActionM () -> ScottyM ()

Specify an action to take if nothing else is found. Note: this _always_ matches, so should generally be the last route specified.

Route Patterns

capture :: String -> RoutePattern

Standard Sinatra-style route. Named captures are prepended with colons. This is the default route type generated by OverloadedString routes. i.e.

 get (capture "/foo/:bar") $ ...

and

 {-# LANGUAGE OverloadedStrings #-}
 ...
 get "/foo/:bar" $ ...

are equivalent.

regex :: String -> RoutePattern

Match requests using a regular expression. Named captures are not yet supported.

 get (regex "^/f(.*)r$") $ do
    path <- param "0"
    cap <- param "1"
    text $ mconcat ["Path: ", path, "\nCapture: ", cap]
>>> curl http://localhost:3000/foo/bar
Path: /foo/bar
Capture: oo/ba

function :: (Request -> Maybe [Param]) -> RoutePattern

Build a route based on a function which can match using the entire Request object. Nothing indicates the route does not match. A Just value indicates a successful match, optionally returning a list of key-value pairs accessible by param.

 get (function $ \req -> Just [("version", pack $ show $ httpVersion req)]) $ do
     v <- param "version"
     text v
>>> curl http://localhost:3000/
HTTP/1.1

literal :: String -> RoutePattern

Build a route that requires the requested path match exactly, without captures.

Accessing the Request, Captures, and Query Parameters

request :: ActionM Request

Get the Request object.

reqHeader :: Text -> ActionM (Maybe Text)

Get a request header. Header name is case-insensitive.

body :: ActionM ByteString

Get the request body.

param :: Parsable a => Text -> ActionM a

Get a parameter. First looks in captures, then form data, then query parameters.

  • Raises an exception which can be caught by rescue if parameter is not found.
  • If parameter is found, but read fails to parse to the correct type, next is called. This means captures are somewhat typed, in that a route won't match if a correctly typed capture cannot be parsed.

params :: ActionM [Param]

Get all parameters from capture, form and query (in that order).

jsonData :: FromJSON a => ActionM a

Parse the request body as a JSON object and return it. Raises an exception if parse is unsuccessful.

files :: ActionM [File]

Get list of uploaded files.

Modifying the Response and Redirecting

status :: Status -> ActionM ()

Set the HTTP response status. Default is 200.

addHeader :: Text -> Text -> ActionM ()

Add to the response headers. Header names are case-insensitive.

setHeader :: Text -> Text -> ActionM ()

Set one of the response headers. Will override any previously set value for that header. Header names are case-insensitive.

redirect :: Text -> ActionM a

Redirect to given URL. Like throwing an uncatchable exception. Any code after the call to redirect will not be run.

 redirect "http://www.google.com"

OR

 redirect "/foo/bar"

Setting Response Body

Note: only one of these should be present in any given route definition, as they completely replace the current Response body.

text :: Text -> ActionM ()

Set the body of the response to the given Text value. Also sets "Content-Type" header to "text/plain".

html :: Text -> ActionM ()

Set the body of the response to the given Text value. Also sets "Content-Type" header to "text/html".

file :: FilePath -> ActionM ()

Send a file as the response. Doesn't set the "Content-Type" header, so you probably want to do that on your own with header.

json :: ToJSON a => a -> ActionM ()

Set the body of the response to the JSON encoding of the given value. Also sets "Content-Type" header to "application/json".

source :: Source (ResourceT IO) (Flush Builder) -> ActionM ()

Set the body of the response to a Source. Doesn't set the "Content-Type" header, so you probably want to do that on your own with header.

raw :: ByteString -> ActionM ()

Set the body of the response to the given ByteString value. Doesn't set the "Content-Type" header, so you probably want to do that on your own with header.

Exceptions

raise :: Text -> ActionM a

Throw an exception, which can be caught with rescue. Uncaught exceptions turn into HTTP 500 responses.

rescue :: ActionM a -> (Text -> ActionM a) -> ActionM a

Catch an exception thrown by raise.

 raise "just kidding" `rescue` (\msg -> text msg)

next :: ActionM a

Abort execution of this action and continue pattern matching routes. Like an exception, any code after next is not executed.

As an example, these two routes overlap. The only way the second one will ever run is if the first one calls next.

 get "/foo/:number" $ do
   n <- param "number"
   unless (all isDigit n) $ next
   text "a number"

 get "/foo/:bar" $ do
   bar <- param "bar"
   text "not a number"

Parsing Parameters

type Param = (Text, Text)

class Parsable a where

Minimum implemention: parseParam

Methods

parseParam :: Text -> Either Text a

Take a Text value and parse it as a, or fail with a message.

parseParamList :: Text -> Either Text [a]

Default implementation parses comma-delimited lists.

 parseParamList t = mapM parseParam (T.split (== ',') t)

Instances

Parsable Bool 
Parsable Char

Overrides default parseParamList to parse String.

Parsable Double 
Parsable Float 
Parsable Int 
Parsable Integer 
Parsable ()

Checks if parameter is present and is null-valued, not a literal '()'. If the URI requested is: '/foo?bar=()&baz' then baz will parse as (), where bar will not.

Parsable ByteString 
Parsable Text 
Parsable a => Parsable [a] 

readEither :: Read a => Text -> Either Text a

Useful for creating Parsable instances for things that already implement Read. Ex:

 instance Parsable Int where parseParam = readEither

Types

type ScottyM a = ScottyT IO a

type ActionM a = ActionT IO a