Open reygoch opened 6 years ago
As you can see in the haddocks for FormUrlEncoded
(in the list of instances), we now require that haskell types implement the FromForm
/ToForm
classes, from the http-api-data
package.
As you can see, FromForm only wants an Either out. So you can do all the validation you want as part of your instance as long as you can come back to an Either
in the end. I'm not an expert in the fancy validation libraries but this sounds like something you should be able to do with those, no? The decoding necessarily has to be pure so you can't perform arbitrary monadic validation there, unless you can turn it all back into a good old Either. If you need some effects for the validation code or other fancy things, you're better off doing some dummy decoding in the FromForm instance and doing the fancy validation as part of your handler. You could so something like:
data Validation = Validated | NotValidated
data Person (v :: Validation) = Person String Int
-- we don't want to decode a "validated person", it has to go through
-- the validation process
instance FromForm (Person 'NotValidated) where
...
-- validate a person, erroring out if validation fails.
-- this assumes the monad has some notion of error. if not, return
-- Either ValidationError (Person 'Validated).
validatePerson :: Person 'NotValidated -> SomeMonad (Person 'Validated)
validatePerson (Person name age)
| age >= 0 = pure (Person name age)
| otherwise = ... -- error out
Note that https://github.com/search?o=desc&q=%22FormUrlEncoded%22+servant&s=indexed&type=Code shows quite a few examples of writing code that uses the FormUrlEncoded content type but I agree that ideally we would have a little cookbook recipe dedicated to this topic. If that's something you're interested in writing once this is all done and figured out, we would be delighted to take a PR :)
Hopefully some things I said will help but let us know if that's not enough.
@alpmestan thanks, I missed the FromForm
, but the thing is it's not very useful for my usecase. I can't really check the database since its result is Either
and also, Either
has to be of type Either Text a
.
When I evaluate my form I get Monad m => m (View v, Maybe a)
so ideally I'd be able to get Either (View v) a
from FromForm
where v
is a structured tree of fields and corresponding errors that can be rendered as an html form and display errors to the user or I can process the input further. This would mean that my handler signature should look something like this : Either (View v) a -> Handler (Page (Credentials))
.
Is it by any chance possible to "chain" handlers? That way I'd be able to Post raw request data into one handler, process it in my custom monad and than forward that result to the final handler in the chain.
Can you maybe point me in the right direction with this? I'd be glad to write a comprehensive tutorial / cookbook once I figure a nice way to do this.
@alpmestan on second thought, I guess I can just write a combinator that gives me a raw form data which I can then pass to my handler and process. Handler signature will be something like RawForm -> Page (Either (Bad Credentials) (Good Credentials))
.
Various observations:
Note: that forall m. Monad m => m a
is the same as a
, as you can pick as witnessed by Identity
and return
.
@reygoch do you have any complete example for how you integrated servant with digestiv-functors? I can't figure this out, and I can't find any servant web form code examples.
Hi,
So, I'm trying to validate a form on my application with digestive functors but I can't find a good way to successfully do that.
I've read #236 but I can't find
FromFormUrlEncoded
class. Was it removed? If so, what is the alternative?I have a
Credentials
data type that carries username and password. It should be validated to make sure password is strong enough and username is not empty, and it should also check if user with that username already exists.I've made a simple wrapper type called 'Page' that contains some meta data about the page that is being rendered and the main content of the page.
And I have a
Credentials
type with the accompanying form.Idea is to have something like this:
where user first gets the empty form, and than it can do a post request to the server where input data is validated (within a custom monad if possible) and than result is either a
GoodForm
which than inserts a new user into the database and returns a success page, or aBadForm
which renders HTML with form containing error warnings.I haven't really seen many (or any) tutorials about form validation in servant which seems like a very important thing, so I'm a bit overwhelmed with this task.