Incorrect user input after POST / Redirect / GET using Yesod form

I recently came across the following problem:
Using yesod I wanted

  • Display the applicative form and give the POST user their data to the special handler
  • In FormFailure redirect the browser to the previous page
  • Print error messages ...
  • ... and redisplay the form already filled in with the wrong data provided by the user

As far as I understand, this is what POST/Redirect/GET .

While points <1-3> required a simple and simple implementation, I found it impossible to reach point <4>!
The yesod-form package automatically handles this problem, but does not allow forwarding between parsing forms <2> and error handling <3,4>, as I would like it to be.

Answer to Michael Snoiman Answer

You suggest that I serialize the provided data and somehow enter it into the form after the redirect. This leads to more detailed questions:

  • How to get data for serialization?

    I know that I can use runRequestBody :: GHandler sm RequestBodyContents , but which are relevant information (field :: GHandler sm RequestBodyContents are generated automatically)?

  • How to insert data into a form?

    If you look at the type, for example. aopt

     aopt :: Field sub master a -> FieldSettings master -> Maybe (Maybe a) -> AForm sub master (Maybe a) 

    You will see that it requires that the default value be of the same type as Field , so it is not possible to re-insert data provided by the user that may not be processed correctly.

    Example: user enters 'A' in intField . Now I want to be able to display "A" in the same field after the redirect, but the API does not allow me.

How do I solve this problem?

+8
post-redirect-get haskell forms yesod
source share
2 answers

I personally consider it acceptable to return a completed form with a POST request, for which the Yesod form API has been optimized. If you want to force redirect even if the form submission fails, you will need to serialize the provided data and save it somewhere, for example:

  • In the database.
  • In user session.
  • As part of the query string parameters for the URL you are redirecting to. Please note that this approach is not suitable for sensitive data, as any intermediate proxies cache form data.
+2
source share

An old question, but I need it today, so maybe post it to others facing the same problem.

Basically, as Michael suggests, we can serialize data per session. This is difficult to do, plus getting it into shape is even more difficult. I had to rip postEnv and postHelper from Yesod.Form.Functions , since they are not exported, but are necessary for this.

You can then use setLastInvalidPost in your handler before the redirect, and then use generateFormFromLastPost in the target handler.

Note that it would be better to use something like Data.Serialize for serialization; however, the Show / Read instances were good enough for my needs (and much simpler).

Here is some good stuff. If you want a complete working snippet, you can test your meaning .

 -- Create a form from last post data in the session if exists, otherwise create a blank form. generateFormFromLastPost :: (RenderMessage (HandlerSite m) FormMessage, MonadHandler m) => (Markup -> MForm m (FormResult a, xml)) -> m (xml, Enctype) generateFormFromLastPost form = do env <- getLastInvalidPost case env of Nothing -> generateFormPost form Just _ -> first snd <$> postHelper form env lastInvalidPostSessionKey :: Text lastInvalidPostSessionKey = "lastInvalidPost" -- Sets the post data retreived from postEnv, ignoring the FileEnv. setLastInvalidPost :: MonadHandler m => Maybe (Env, FileEnv) -> m () setLastInvalidPost Nothing = return () setLastInvalidPost (Just (env, _)) = sessionSetter lastInvalidPostSessionKey env -- Retrieves the previous post data to be passed to postHelper. getLastInvalidPost :: MonadHandler m => m (Maybe (Env, FileEnv)) getLastInvalidPost = do result <- sessionGetter lastInvalidPostSessionKey return $ case result of Nothing -> Nothing Just env -> Just (env, Map.fromList []) sessionSetter :: (MonadHandler m, Show a) => Text -> a -> m () sessionSetter key = setSession key . pack . show sessionGetter :: (MonadHandler m, Read b) => Text -> m (Maybe b) sessionGetter key = do m <- lookupSession key return $ readMaybe . unpack =<< m 
+2
source share

All Articles