veb: deprecate vweb (part 2); move all vweb examples to veb
@ -289,7 +289,7 @@ V comes with a version of mbedtls, which should work on all systems. If you find
|
||||
use OpenSSL instead, you will need to make sure that it is installed on your system, then
|
||||
use the `-d use_openssl` switch when you compile.
|
||||
|
||||
Note: Mbed-TLS is smaller and easier to install on windows too (V comes with it), but if you
|
||||
Note: Mbed-TLS is smaller and easier to install on windows too (V comes with it), but if you
|
||||
write programs, that do lots of http requests to HTTPS/SSL servers, in most cases, it is *best*
|
||||
to compile with `-d use_openssl`, and do so on a system, where you do have OpenSSL installed
|
||||
(see below). Mbed-TLS is slower, and can have more issues, especially when you are doing parallel
|
||||
@ -363,7 +363,7 @@ With V's `vab` tool, building V UI and graphical apps for Android can become as
|
||||
## Developing web applications
|
||||
|
||||
Check out the
|
||||
[Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md)
|
||||
[Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/README.md)
|
||||
tutorial and Gitly, a light and fast alternative to GitHub/GitLab:
|
||||
|
||||
https://github.com/vlang/gitly
|
||||
|
@ -115,7 +115,7 @@ fn run_vsh_script_tcc() {
|
||||
}
|
||||
|
||||
fn test_v_tutorials_tcc() {
|
||||
exec('v tutorials/building_a_simple_web_blog_with_vweb/code/blog')
|
||||
exec('v tutorials/building_a_simple_web_blog_with_veb/code/blog')
|
||||
}
|
||||
|
||||
fn build_fast_tcc() {
|
||||
@ -228,7 +228,7 @@ fn build_tetris_autofree_gcc() {
|
||||
}
|
||||
|
||||
fn build_blog_autofree_gcc() {
|
||||
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog')
|
||||
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_veb/code/blog')
|
||||
}
|
||||
|
||||
fn build_option_test_autofree_gcc() {
|
||||
|
@ -76,7 +76,7 @@ fn build_tetris_autofree() {
|
||||
}
|
||||
|
||||
fn build_blog_autofree() {
|
||||
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog')
|
||||
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_veb/code/blog')
|
||||
}
|
||||
|
||||
fn build_examples_prod() {
|
||||
|
40
examples/veb/file_upload/file_uploading.v
Normal file
@ -0,0 +1,40 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
|
||||
const port = 8082
|
||||
|
||||
struct App {
|
||||
}
|
||||
|
||||
struct Context {
|
||||
veb.Context
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
veb.run[App, Context](mut app, port)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() veb.Result {
|
||||
return $veb.html()
|
||||
}
|
||||
|
||||
@['/upload'; post]
|
||||
pub fn (mut app App) upload() veb.Result {
|
||||
dump(ctx.form)
|
||||
dump(ctx.files)
|
||||
fdata := ctx.files['upfile']
|
||||
mut files := []veb.RawHtml{}
|
||||
for d in fdata {
|
||||
files << d.data.replace_each(['\n', '<br>', '\n\r', '<br>', '\t', ' ', ' ', ' '])
|
||||
}
|
||||
return $veb.html()
|
||||
}
|
||||
|
||||
@['/submit'; post]
|
||||
pub fn (mut app App) submit() veb.Result {
|
||||
dump(ctx.form)
|
||||
form_data := ctx.form.clone()
|
||||
return $veb.html()
|
||||
}
|
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 671 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
44
examples/veb/server_sent_events/server.v
Normal file
@ -0,0 +1,44 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import rand
|
||||
import time
|
||||
import veb
|
||||
import veb.sse
|
||||
|
||||
struct App {
|
||||
veb.StaticHandler
|
||||
}
|
||||
|
||||
struct Context {
|
||||
veb.Context
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
app.serve_static('/favicon.ico', 'favicon.ico')!
|
||||
app.mount_static_folder_at(os.resource_abs_path('.'), '/')!
|
||||
veb.run[App, Context](mut app, 8081)
|
||||
}
|
||||
|
||||
// XTODO template broken (@)
|
||||
pub fn (mut app App) index() veb.Result {
|
||||
title := 'SSE Example'
|
||||
return $veb.html()
|
||||
}
|
||||
|
||||
fn (mut app App) sse() veb.Result {
|
||||
mut session := sse.start_connection(mut ctx.Context)
|
||||
// Note: you can setup session.write_timeout and session.headers here
|
||||
// session.start() or { return app.server_error(501) }
|
||||
session.send_message(data: 'ok') or { return ctx.server_error_with_status(.not_implemented) }
|
||||
for {
|
||||
data := '{"time": "${time.now().str()}", "random_id": "${rand.ulid()}"}'
|
||||
session.send_message(event: 'ping', data: data) or {
|
||||
return ctx.server_error_with_status(.not_implemented)
|
||||
}
|
||||
println('> sent event: ${data}')
|
||||
time.sleep(1 * time.second)
|
||||
}
|
||||
return ctx.server_error_with_status(.not_implemented)
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
module main
|
||||
|
||||
import vweb
|
||||
|
||||
const port = 8082
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
}
|
||||
|
||||
fn main() {
|
||||
vweb.run(&App{}, port)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/upload'; post]
|
||||
pub fn (mut app App) upload() vweb.Result {
|
||||
dump(app.form)
|
||||
dump(app.files)
|
||||
fdata := app.files['upfile']
|
||||
mut files := []vweb.RawHtml{}
|
||||
for d in fdata {
|
||||
files << d.data.replace_each(['\n', '<br>', '\n\r', '<br>', '\t', ' ', ' ', ' '])
|
||||
}
|
||||
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/submit'; post]
|
||||
pub fn (mut app App) submit() vweb.Result {
|
||||
dump(app.form)
|
||||
form_data := app.form.clone()
|
||||
return $vweb.html()
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import rand
|
||||
import time
|
||||
import vweb
|
||||
import vweb.sse
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
app.serve_static('/favicon.ico', 'favicon.ico')
|
||||
app.mount_static_folder_at(os.resource_abs_path('.'), '/')
|
||||
vweb.run(app, 8081)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
title := 'SSE Example'
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
fn (mut app App) sse() vweb.Result {
|
||||
mut session := sse.new_connection(app.conn)
|
||||
// Note: you can setup session.write_timeout and session.headers here
|
||||
session.start() or { return app.server_error(501) }
|
||||
session.send_message(data: 'ok') or { return app.server_error(501) }
|
||||
for {
|
||||
data := '{"time": "${time.now().str()}", "random_id": "${rand.ulid()}"}'
|
||||
session.send_message(event: 'ping', data: data) or { return app.server_error(501) }
|
||||
println('> sent event: ${data}')
|
||||
time.sleep(1 * time.second)
|
||||
}
|
||||
return app.server_error(501)
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
const messageList = document.getElementById('message-list');
|
||||
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const socket = new WebSocket(`${protocol}://${location.host}/ws`);
|
||||
let i = 0;
|
||||
|
||||
function send(message) {
|
||||
messageList.innerHTML += `<li>> ${message}</li>`;
|
||||
socket.send(message);
|
||||
}
|
||||
|
||||
socket.addEventListener("open", (event) => {
|
||||
console.log('Connected to WS server');
|
||||
send('Hey everyone !');
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const { data } = event;
|
||||
messageList.innerHTML += `<li>< ${data}</li>`;
|
||||
setTimeout(() => {
|
||||
send(`Roger ${i++}`);
|
||||
}, 3000);
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang=en>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>vweb websocket example page</title>
|
||||
</head>
|
||||
<body>
|
||||
<ol id="message-list"></ol>
|
||||
<script type="text/javascript" src="websocket_client.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,75 +0,0 @@
|
||||
module main
|
||||
|
||||
import log
|
||||
import net.http
|
||||
import net.websocket
|
||||
import term
|
||||
import vweb
|
||||
|
||||
const http_port = 8080
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
mut:
|
||||
wss &websocket.Server @[vweb_global]
|
||||
}
|
||||
|
||||
fn slog(message string) {
|
||||
eprintln(term.colorize(term.bright_yellow, message))
|
||||
}
|
||||
|
||||
fn clog(message string) {
|
||||
eprintln(term.colorize(term.cyan, message))
|
||||
}
|
||||
|
||||
fn wlog(message string) {
|
||||
eprintln(term.colorize(term.bright_blue, message))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := new_app() or { panic(err) }
|
||||
vweb.run(app, http_port)
|
||||
}
|
||||
|
||||
fn new_app() !&App {
|
||||
mut app := &App{
|
||||
wss: new_websocker_server()!
|
||||
}
|
||||
app.handle_static('assets', true)
|
||||
return app
|
||||
}
|
||||
|
||||
fn new_websocker_server() !&websocket.Server {
|
||||
mut logger := &log.Log{}
|
||||
logger.set_level(.debug)
|
||||
mut wss := websocket.new_server(.ip, 8080, '', logger: logger)
|
||||
wss.on_connect(fn (mut server_client websocket.ServerClient) !bool {
|
||||
slog('ws.on_connect, server_client.client_key: ${server_client.client_key}')
|
||||
return true
|
||||
})!
|
||||
wss.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
|
||||
slog('s.on_message msg.opcode: ${msg.opcode} | msg.payload: ${msg.payload}')
|
||||
ws.write(msg.payload, msg.opcode) or {
|
||||
eprintln('ws.write err: ${err}')
|
||||
return err
|
||||
}
|
||||
})
|
||||
wss.on_close(fn (mut ws websocket.Client, code int, reason string) ! {
|
||||
slog('s.on_close code: ${code}, reason: ${reason}')
|
||||
})
|
||||
slog('Websocket Server initialized')
|
||||
return wss
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
pub fn (mut app App) ws() !vweb.Result {
|
||||
key := app.req.header.get(http.CommonHeader.sec_websocket_key)!
|
||||
app.wss.handle_handshake(mut app.conn, key) or {
|
||||
wlog('handle_handshake error: ${err.msg()}')
|
||||
return err
|
||||
}
|
||||
return app.text('')
|
||||
}
|
@ -83,7 +83,7 @@ Running a Veb app on http://localhost:8081 ...
|
||||
|
||||
Veb helpfully provided a link, open http://localhost:8081/ in your browser:
|
||||
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png?raw=true">
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/hello.png?raw=true">
|
||||
|
||||
The App struct holds shared application data, while Context handles per-request data and embeds
|
||||
`veb.Context` for response methods like `.text()`.
|
||||
@ -109,7 +109,7 @@ fn (mut app App) time() veb.Result {
|
||||
|
||||
Custom routes can be defined using attributes like @['/index'].
|
||||
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/time.png?raw=true">
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/time.png?raw=true">
|
||||
|
||||
> TIP: run the following command to live-reload the server: `v watch run blog.v`
|
||||
|
||||
@ -143,7 +143,7 @@ pub fn (mut app App) index() veb.Result {
|
||||
}
|
||||
```
|
||||
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png?raw=true">
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/hello_html.png?raw=true">
|
||||
|
||||
Good, now we have an actual HTML page.
|
||||
|
||||
@ -278,7 +278,7 @@ Finally, let's update our view:
|
||||
v run .
|
||||
```
|
||||
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png?raw=true">
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/articles1.png?raw=true">
|
||||
|
||||
That was very simple, wasn't it?
|
||||
|
||||
@ -398,7 +398,7 @@ pub fn (mut app App) articles() veb.Result {
|
||||
}
|
||||
```
|
||||
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png?raw=true">
|
||||
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/articles_json.png?raw=true">
|
||||
|
||||
### Persistent data
|
||||
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
@ -117,6 +117,7 @@ pub const mime_types = {
|
||||
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
||||
'.rar': 'application/vnd.rar'
|
||||
'.rtf': 'application/rtf'
|
||||
'.scss': 'text/css'
|
||||
'.sh': 'application/x-sh'
|
||||
'.svg': 'image/svg+xml'
|
||||
'.swf': 'application/x-shockwave-flash'
|
||||
|
@ -227,6 +227,12 @@ pub fn (mut ctx Context) server_error(msg string) Result {
|
||||
return ctx.send_response_to_client('text/plain', msg)
|
||||
}
|
||||
|
||||
// send an error with a custom status
|
||||
pub fn (mut ctx Context) server_error_with_status(s http.Status) Result {
|
||||
ctx.res.set_status(s)
|
||||
return ctx.send_response_to_client('text/plain', 'Server error')
|
||||
}
|
||||
|
||||
// send a 204 No Content response without body and content-type
|
||||
pub fn (mut ctx Context) no_content() Result {
|
||||
ctx.res.set_status(.no_content)
|
||||
|