Fork of vweb with data modeling and validation.
This is a proof of concept.
mkdir modules
cd modules
git clone https://github.com/intangere/tea.git
Probably all of Vweb's normal usage applies.
This is just a thin wrapper on top of Vweb that exposes enough to allow for changing route signatures.
There is plenty of examples in example.v
A simple complete example is as follows:
Let's create a /login
route which takes in a user model.
First import tea and declare your app variable as such:
import tea
struct App {
tea.Context
pub mut:
validators tea.Validators<App>
}
Define your user
model:
struct User {
username string
password string
}
Define a function to check the user
model is valid:
fn (user User) is_valid() bool {
username := user.username.strip_margin()
password := user.password.strip_margin()
return username.len > 3 &&
password.len > 8
}
Define your /login
route:
['/login'; post]
fn (mut app App) login(user User) tea.Result {
// posted user parameters will be available in user after being validated
println('Username: ' + user.username + ' Password: ' + user.password)
app.json('{"status":"testing"}')
}
Define your main function:
fn main() {
mut app := App{}
}
Now in that main function you can create your user
data validator.
(You could define this as a named function too)
login_validator := fn (mut app App) {
model := tea.decode_model<User>(app.req.data)
if !model.is_valid() {
app.validation_error('username or password too short')
return
}
app.login(model)
}
The data validator should unpack some sort of data (in this case the request json body), validate it, and depending on the result, return a validation error or pass the validated model(s) to the route.
Now app needs to be aware of the validator and which route to run it on. So after declaring the validator you need to add it to app.validators
:
app.validators.validators['/login'] = login_validator
Then just run the app like you would with Vweb:
tea.run(&app, 8080)
/user/:user_id
with other parameters, the url parameter user_id
must be after the other parameters. i.e// this will not compile (with or without a validator defined. However it does require one)
['/user/:user_id']
fn (mut app App) user(user_id string, user User) {
}
// this will compile with or without a validator
['/user/:user_id']
fn (mut app App) user(user_id string) {
}
// this will compile with a validator
['/user/:user_id']
fn (mut app App) user(user_id int) {
}
// this is how it has to be defined and will compile (requires a validator defined for /user/:user_id)
['/user/:user_id']
fn (mut app App) user(user User, user_id string) {
}
tea.decode_model<T>(string)
takes a json string as input and decodes it into a structapp.url_params
exposes the url path parameters when applicable as a map[string]string
:user_id
as a parameter but still access ittea.from_map<T>(map[string]string)
can be used to take app.query and turn it into a struct with string only fieldsWhenever compile-time reflection is more complete in V something like this should be possible which would hide most of the internals:
import tea
struct App {
tea.Context
pub mut:
validators tea.Validators<App>
}
['/login'; post]
fn (mut app App) login(user User) tea.Result {
// posted user parameters will be available in user after being validated
println('Username: ' + user.username + ' Password: ' + user.password)
app.json('{"status":"testing"}')
}
['/login'; validator]
fn (mut app App) login_validator() User {
model := tea.decode_model<User>(app.req.data)
if !model.is_valid() {
app.validation_error('username or password too short')
return
}
return model
}
fn main() {
mut app := App{}
tea.run(&app, 8080)
}