mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
vweb: host attribute (#18288)
This commit is contained in:
parent
f22ba836fd
commit
2904c399b5
@ -251,6 +251,24 @@ pub fn (mut app App) controller_get_user_by_id() vweb.Result {
|
|||||||
return app.text(app.query.str())
|
return app.text(app.query.str())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
#### - Host
|
||||||
|
To restrict an endpoint to a specific host, you can use the `host` attribute
|
||||||
|
followed by a colon `:` and the host name.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
['/'; host: 'example.com']
|
||||||
|
pub fn (mut app App) hello_web() vweb.Result {
|
||||||
|
return app.text('Hello World')
|
||||||
|
}
|
||||||
|
|
||||||
|
['/'; host: 'api.example.org']
|
||||||
|
pub fn (mut app App) hello_api() vweb.Result {
|
||||||
|
return app.text('Hello API')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
Vweb has different kinds of middleware.
|
Vweb has different kinds of middleware.
|
||||||
|
@ -4,15 +4,16 @@ import net.urllib
|
|||||||
import net.http
|
import net.http
|
||||||
|
|
||||||
// Parsing function attributes for methods and path.
|
// Parsing function attributes for methods and path.
|
||||||
fn parse_attrs(name string, attrs []string) !([]http.Method, string, string) {
|
fn parse_attrs(name string, attrs []string) !([]http.Method, string, string, string) {
|
||||||
if attrs.len == 0 {
|
if attrs.len == 0 {
|
||||||
return [http.Method.get], '/${name}', ''
|
return [http.Method.get], '/${name}', '', ''
|
||||||
}
|
}
|
||||||
|
|
||||||
mut x := attrs.clone()
|
mut x := attrs.clone()
|
||||||
mut methods := []http.Method{}
|
mut methods := []http.Method{}
|
||||||
mut middleware := ''
|
mut middleware := ''
|
||||||
mut path := ''
|
mut path := ''
|
||||||
|
mut host := ''
|
||||||
|
|
||||||
for i := 0; i < x.len; {
|
for i := 0; i < x.len; {
|
||||||
attr := x[i]
|
attr := x[i]
|
||||||
@ -36,6 +37,11 @@ fn parse_attrs(name string, attrs []string) !([]http.Method, string, string) {
|
|||||||
x.delete(i)
|
x.delete(i)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if attr.starts_with('host:') {
|
||||||
|
host = attr.all_after('host:').trim_space()
|
||||||
|
x.delete(i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
if x.len > 0 {
|
if x.len > 0 {
|
||||||
@ -49,8 +55,8 @@ fn parse_attrs(name string, attrs []string) !([]http.Method, string, string) {
|
|||||||
if path == '' {
|
if path == '' {
|
||||||
path = '/${name}'
|
path = '/${name}'
|
||||||
}
|
}
|
||||||
// Make path lowercase for case-insensitive comparisons
|
// Make path and host lowercase for case-insensitive comparisons
|
||||||
return methods, path.to_lower(), middleware
|
return methods, path.to_lower(), middleware, host.to_lower()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_query_from_url(url urllib.URL) map[string]string {
|
fn parse_query_from_url(url urllib.URL) map[string]string {
|
||||||
|
123
vlib/vweb/vweb.v
123
vlib/vweb/vweb.v
@ -183,6 +183,7 @@ struct Route {
|
|||||||
methods []http.Method
|
methods []http.Method
|
||||||
path string
|
path string
|
||||||
middleware string
|
middleware string
|
||||||
|
host string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defining this method is optional.
|
// Defining this method is optional.
|
||||||
@ -399,7 +400,7 @@ fn generate_routes[T](app &T) !map[string]Route {
|
|||||||
// Parsing methods attributes
|
// Parsing methods attributes
|
||||||
mut routes := map[string]Route{}
|
mut routes := map[string]Route{}
|
||||||
$for method in T.methods {
|
$for method in T.methods {
|
||||||
http_methods, route_path, middleware := parse_attrs(method.name, method.attrs) or {
|
http_methods, route_path, middleware, host := parse_attrs(method.name, method.attrs) or {
|
||||||
return error('error parsing method attributes: ${err}')
|
return error('error parsing method attributes: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,12 +408,13 @@ fn generate_routes[T](app &T) !map[string]Route {
|
|||||||
methods: http_methods
|
methods: http_methods
|
||||||
path: route_path
|
path: route_path
|
||||||
middleware: middleware
|
middleware: middleware
|
||||||
|
host: host
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
type ControllerHandler = fn (ctx Context, mut url urllib.URL, tid int)
|
type ControllerHandler = fn (ctx Context, mut url urllib.URL, host string, tid int)
|
||||||
|
|
||||||
pub struct ControllerPath {
|
pub struct ControllerPath {
|
||||||
path string
|
path string
|
||||||
@ -436,12 +438,12 @@ pub fn controller[T](path string, global_app &T) &ControllerPath {
|
|||||||
// no need to type `ControllerHandler` as generic since it's not needed for closures
|
// no need to type `ControllerHandler` as generic since it's not needed for closures
|
||||||
return &ControllerPath{
|
return &ControllerPath{
|
||||||
path: path
|
path: path
|
||||||
handler: fn [global_app, path, routes] [T](ctx Context, mut url urllib.URL, tid int) {
|
handler: fn [global_app, path, routes] [T](ctx Context, mut url urllib.URL, host string, tid int) {
|
||||||
// request_app is freed in `handle_route`
|
// request_app is freed in `handle_route`
|
||||||
mut request_app := new_request_app[T](global_app, ctx, tid)
|
mut request_app := new_request_app[T](global_app, ctx, tid)
|
||||||
// transform the url
|
// transform the url
|
||||||
url.path = url.path.all_after_first(path)
|
url.path = url.path.all_after_first(path)
|
||||||
handle_route[T](mut request_app, url, &routes, tid)
|
handle_route[T](mut request_app, url, host, &routes, tid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -620,6 +622,8 @@ fn handle_conn[T](mut conn net.TcpConn, global_app &T, routes &map[string]Route,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host := req.header.get(http.CommonHeader.host) or { '' }.to_lower()
|
||||||
|
|
||||||
// Create Context with request data
|
// Create Context with request data
|
||||||
ctx := Context{
|
ctx := Context{
|
||||||
req: req
|
req: req
|
||||||
@ -635,18 +639,18 @@ fn handle_conn[T](mut conn net.TcpConn, global_app &T, routes &map[string]Route,
|
|||||||
for controller in global_app.controllers {
|
for controller in global_app.controllers {
|
||||||
if url.path.len >= controller.path.len && url.path.starts_with(controller.path) {
|
if url.path.len >= controller.path.len && url.path.starts_with(controller.path) {
|
||||||
// pass route handling to the controller
|
// pass route handling to the controller
|
||||||
controller.handler(ctx, mut url, tid)
|
controller.handler(ctx, mut url, host, tid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mut request_app := new_request_app(global_app, ctx, tid)
|
mut request_app := new_request_app(global_app, ctx, tid)
|
||||||
handle_route(mut request_app, url, routes, tid)
|
handle_route(mut request_app, url, host, routes, tid)
|
||||||
}
|
}
|
||||||
|
|
||||||
[manualfree]
|
[manualfree]
|
||||||
fn handle_route[T](mut app T, url urllib.URL, routes &map[string]Route, tid int) {
|
fn handle_route[T](mut app T, url urllib.URL, host string, routes &map[string]Route, tid int) {
|
||||||
defer {
|
defer {
|
||||||
unsafe {
|
unsafe {
|
||||||
free(app)
|
free(app)
|
||||||
@ -690,70 +694,77 @@ fn handle_route[T](mut app T, url urllib.URL, routes &map[string]Route, tid int)
|
|||||||
// Used for route matching
|
// Used for route matching
|
||||||
route_words := route.path.split('/').filter(it != '')
|
route_words := route.path.split('/').filter(it != '')
|
||||||
|
|
||||||
// Route immediate matches first
|
// Skip if the host does not match or is empty
|
||||||
// For example URL `/register` matches route `/:user`, but `fn register()`
|
if route.host == '' || route.host == host {
|
||||||
// should be called first.
|
// Route immediate matches first
|
||||||
if !route.path.contains('/:') && url_words == route_words {
|
// For example URL `/register` matches route `/:user`, but `fn register()`
|
||||||
// We found a match
|
// should be called first.
|
||||||
$if T is MiddlewareInterface {
|
if !route.path.contains('/:') && url_words == route_words {
|
||||||
if validate_middleware(mut app, url.path) == false {
|
// We found a match
|
||||||
return
|
$if T is MiddlewareInterface {
|
||||||
|
if validate_middleware(mut app, url.path) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.req.method == .post && method.args.len > 0 {
|
||||||
|
// Populate method args with form values
|
||||||
|
mut args := []string{cap: method.args.len}
|
||||||
|
for param in method.args {
|
||||||
|
args << app.form[param.name]
|
||||||
|
}
|
||||||
|
|
||||||
|
if route.middleware == '' {
|
||||||
|
app.$method(args)
|
||||||
|
} else if validate_app_middleware(mut app, route.middleware,
|
||||||
|
method.name)
|
||||||
|
{
|
||||||
|
app.$method(args)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if route.middleware == '' {
|
||||||
|
app.$method()
|
||||||
|
} else if validate_app_middleware(mut app, route.middleware,
|
||||||
|
method.name)
|
||||||
|
{
|
||||||
|
app.$method()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.req.method == .post && method.args.len > 0 {
|
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
|
||||||
// Populate method args with form values
|
$if T is MiddlewareInterface {
|
||||||
mut args := []string{cap: method.args.len}
|
if validate_middleware(mut app, url.path) == false {
|
||||||
for param in method.args {
|
return
|
||||||
args << app.form[param.name]
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if route.middleware == '' {
|
|
||||||
app.$method(args)
|
|
||||||
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
|
||||||
app.$method(args)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if route.middleware == '' {
|
if route.middleware == '' {
|
||||||
app.$method()
|
app.$method()
|
||||||
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
||||||
app.$method()
|
app.$method()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
|
if params := route_matches(url_words, route_words) {
|
||||||
$if T is MiddlewareInterface {
|
method_args := params.clone()
|
||||||
if validate_middleware(mut app, url.path) == false {
|
if method_args.len != method.args.len {
|
||||||
return
|
eprintln('[vweb] tid: ${tid:03d}, warning: uneven parameters count (${method.args.len}) in `${method.name}`, compared to the vweb route `${method.attrs}` (${method_args.len})')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if route.middleware == '' {
|
|
||||||
app.$method()
|
|
||||||
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
|
||||||
app.$method()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if params := route_matches(url_words, route_words) {
|
$if T is MiddlewareInterface {
|
||||||
method_args := params.clone()
|
if validate_middleware(mut app, url.path) == false {
|
||||||
if method_args.len != method.args.len {
|
return
|
||||||
eprintln('[vweb] tid: ${tid:03d}, warning: uneven parameters count (${method.args.len}) in `${method.name}`, compared to the vweb route `${method.attrs}` (${method_args.len})')
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$if T is MiddlewareInterface {
|
|
||||||
if validate_middleware(mut app, url.path) == false {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
if route.middleware == '' {
|
||||||
|
app.$method(method_args)
|
||||||
|
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
||||||
|
app.$method(method_args)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if route.middleware == '' {
|
|
||||||
app.$method(method_args)
|
|
||||||
} else if validate_app_middleware(mut app, route.middleware, method.name) {
|
|
||||||
app.$method(method_args)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user