[added section on request method matching Jeremy Shaw **20100615200159 Ignore-this: ebdb4bd7c301a85e838c7a439f29c6c2 ] hunk ./Makefile 6 -DEMOS := Dir.lhs Dir2.lhs Dirs.lhs Path.lhs FromReqURI.lhs +ROUTE_FILTER_DEPS := MonadPlus.lhs Dir.lhs Dir2.lhs Dirs.lhs Path.lhs FromReqURI.lhs MethodM.lhs MatchMethod.lhs hunk ./Makefile 8 -SRC := $(MAIN) $(DEPS) $(DEMOS) +SRC := $(MAIN) $(DEPS) $(ROUTE_FILTER_DEPS) hunk ./Makefile 13 -RouteFilters.html: MonadPlus.lhs Dir.lhs Dir2.lhs Dirs.lhs Path.lhs FromReqURI.lhs + +RouteFilters.html: $(ROUTE_FILTER_DEPS) addfile ./MatchMethod.lhs hunk ./MatchMethod.lhs 1 + + +

Advanced method matching with MatchMethod

+ +

The method routing functions use a class (MatchMethod method) instead of the concrete type Method.

+ +
+#ifdef HsColour +> class MatchMethod m where +> matchMethod :: m -> Method -> Bool +> +> instance MatchMethod Method where ... +> instance MatchMethod [Method] where ... +> instance MatchMethod (Method -> Bool) where ... +> instance MatchMethod () where ... +#endif +
+ +

This allows us to easily match on more than one method by either providing a list of acceptable matches, or by providing a function which returns a boolean value. We can use this feature to support the HEAD method. When the client does a HEAD request, the server is supposed to return the same headers it would for a GET request, but with an empty response body. Happstack includes special support for handling this automatically in most cases.

+ +
+ +> module Main where +> +> import Control.Monad (msum) +> import Happstack.Server (Method(GET, HEAD), dir, methodM, nullConf, ok, simpleHTTP) +> +> main :: IO () +> main = simpleHTTP nullConf $ msum +> [ do methodM [GET, HEAD] +> ok $ "Hello, World\n" +> ] +> + +
+ +

We can now use curl to do a normal GET request, or we can +use the -I flag which does a HEAD request:

+ +
+
+ $ curl http://localhost:8000/
+Hello, World
+ $ curl -I http://localhost:8000/
+HTTP/1.1 200 OK
+Connection: Keep-Alive
+Content-Length: 13
+Content-Type: text/plain; charset=UTF-8
+Date: Tue, 15 Jun 2010 19:56:07 GMT
+Server: Happstack/0.5.0
+
+
+ +

Happstack automatically notices that it is a HEAD request, and does not send the body.

addfile ./MethodM.lhs hunk ./MethodM.lhs 1 + + +

Matching on Request Method (GET, POST, etc)

+

methodM and methodOnly

+

We can specify that a route is only valid for specific HTTP request methods by using the methodM guard:

+ +
+#ifdef HsColour +> methodM :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m () +#endif +
+ +

The methodM guard does two things:

+
    +
  1. Specifies what methods to allow
  2. +
  3. Checks that all the path segments have been consumed
  4. +
+ +

Here is a simple demo app:

+
+ +> module Main where +> +> import Control.Monad (msum) +> import Happstack.Server (Method(GET, POST), dir, methodM, nullConf, ok, simpleHTTP) +> +> main :: IO () +> main = simpleHTTP nullConf $ msum +> [ do methodM GET +> ok $ "You did a GET request.\n" +> , do methodM POST +> ok $ "You did a POST request.\n" +> , dir "foo" $ do methodM GET +> ok $ "You did a GET request on /foo\n" +> ] +> + +
+

[Source code for the app is here.]

+ +

Using curl we can see the expected results for normal GET and POST requests to /:

+ +
+
+ $ curl http://localhost:8000/
+You did a GET request.
+ $ curl -d '' http://localhost:8000/
+You did a POST request.
+
+
+ +

Note that methodM also requires that the all the segments of request path have been consumed. We can see in here that /foo is accepted, but not /foo/bar, since only foo is consumed by the, dir "foo" filter.

+ +
+
+ $ curl http://localhost:8000/foo
+You did a GET request on /foo
+ $ curl http://localhost:8000/foo/bar
+<404 message>
+
+
+ +

If we want to allow unconsumed path segments we can use methodOnly instead.

+
+#ifdef HsColour +> methodOnly :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m () +#endif +
+ +

In fact the definition for methodM is simply:

+
+#ifdef HsColour +> methodM :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m () +> methodM meth = methodOnly meth >> nullDir +#endif +
hunk ./RouteFilters.lhs 32 +#include "MethodM.lhs" +#include "MatchMethod.lhs" hunk ./RouteFilters.lhs 57 +

Next: templates and HTML

hunk ./Templates.lhs 49 +
XSLT
hunk ./gen-toc.hs 46 -files = - [ "HelloWorld.html" - , "RouteFilters.html" - ] - - hunk ./theme.css 8 + line-height: 1.5em; + hunk ./theme.css 154 + line-height: 1em;