MFlow  Create and maintain dynamic Web applications as easy and fast as console applications
Thou shall not write request handlers
 This release: 11/06/2014.  agocorona@gmail.com (@agocorona) <>< . issues & bugs .  Mflow Source code,     source code of this site




Web Services in MFlow


Now it is possible to create Web Services.  These three examples below show three ways to create them. The third one uses the Widget monad and process the parameters as if they were a parsec-like parser.  The result is a more compact code. 

The three services implement a sum and a product of two integers. 

First, a REST web service, where the parameters are in a REST path 

main= runNavigation "apirest" . step $ ask $ do
         op <- getRestParam
         term1 <- getRestParam
         term2 <- getRestParam
         case (op, term1,term2) of
           (Just "sum", Just x, Just y) -> wrender (x + y :: Int) **> stop
           (Just "prod", Just x, Just y) -> wrender (x * y) **> stop
           _ ->do -- blaze Html
                     h1 << "ERROR. API usage:"
                     h3 << "http://server/api/sum/[Int]/[Int]"
                     h3 << "http://server/api/prod/[Int]/[Int]"
                  ++> stop


stop = noWidget  does not validate, so ask willl not navigate forward.


The second example read key-value parameters for the numbers instead of REST parameters:

main= runNavigation "apikw" . step $ ask $ do
         op <- getRestParam
         term1 <- getKeyValueParam "t1"
         term2 <- getKeyValueParam "t2"
         case (op, term1,term2) of
           (Just "sum", Just x, Just y) -> wrender (x + y :: Int) **> stop
           (Just "prod", Just x, Just y) -> wrender (x * y) **> stop
           _ ->do -- blaze Html
                     h1 << "ERROR. API usage:"
                     h3 << "http://server/api/sum?t1=[Int]&t2=[Int]"
                     h3 << "http://server/api/prod?t1=[Int]&t2=[Int]"
                  ++> stop

Some example invocation:

http://mflowdemo.herokuapp.com/apikv/prod?t1=4&t2=3



The third service uses key-value parameters as well, but I defined parsec-like combinators that are Widgets as well. That is right, since a widget is both a parser element and a writer. 

The combinators rest and wint are  defined below, from getRestParam and getKeyValueParam. They are now part of the MFlow.Forms.WebApi module

rest verify that the next rest parameter is the value expected. wint read a parameter of type Int with a given key. <?> present a blaze-html formatting when the parser fails, like in a parser combinator. It uses the same applicative, alternative and monadic combinators defined for any other widget in MFlow:

main = runNavigation "apiparser" . step . asks $
             do rest "sum" ; disp $ (+) <$> wint "t1" <*> wint "t2"
         <|> do rest "prod" ; disp $ (*) <$> wint "t1" <*> wint "t2"
         <?> do -- blaze Html
                h1 << "ERROR. API usage:"
                h3 << "http://server/api/sum?t1=[Int]&t2=[Int]"
                h3 << "http://server/api/prod?t1=[Int]&t2=[Int]"
    where
    asks w= ask $ w >> stop


Some example invocation:



I will incorporate some example with JSON output in the next Web service example 

The parser methods are defined here, They are now part of the MFlow.Forms.WebApi module

-- To be added to MFlow.Forms.WebApi.hs --

stop= noWidget

wrestParam = View $ do
   mr <- getRestParam
   return $ FormElm [] mr

rest v= do
   r <- wrestParam
   if r==v then return v else modify (\s -> s{mfPIndex= mfPIndex s-1}) >> stop

wparam par= View $ do
   mr <- getKeyValueParam par
   return $ FormElm [] mr

disp :: Show a => View Html IO a -> View Html IO ()
disp w= View $ do
   elm@(FormElm f mx) <- runView w
   case mx of
     Nothing -> return $ FormElm f Nothing
     justx@(Just x) -> return $ FormElm (f++[fromStr $ show x]) $ return ()



infixl 3 <?>
(<?>) w v= View $ do
  r@(FormElm f mx) <- runView w
  case mx of
    Nothing -> runView $ v ++> stop
    Just _ -> return r

wint p= wparam p :: View Html IO Int

comments powered by Disqus