您的位置:首页 > 编程语言 > Go语言

编写GO的WEB开发框架(三): 如何进行动态路由

2016-03-04 00:00 441 查看
摘要: 对比PHP的常见解决方案,讲述GO如何通过反射方式实现根椐PATH等进行动态规则路由的方法

大部份情况下,开发者并不想每写一个Controller,还要去改写一下路由表,因为这样的话,Controller一多,维护路由表并不是一件讨好的事件。

较好的解决方案是直接由请求的路径(或请求参数)来动态分派到相应的控制器方法,习惯地,这种方式称为动态路由或规则路由,一个明显的特征是自动进行请求路径与控制器方法的匹配。

比如:

请求 /User 表示调用名字为User的Controller方法

请求 /Task 表示调用名字为Task的Controller方法

如果对应的方法不存在,则响应404

(如果需要区分请求方法来作不同的处理,在Controller方法中实现)

在php的实现

常见方式是根椐请求的路径或参数,直接include相应的controller类,然后在new一下就可以:

<?php
$controller = $_GET['c];
$method = $_GET['m'];
$file =  "{$controller}_control.php";
file_exists($file) && include $file;
//如果class或method不存在,输出404
$c = new $controller;
$c -> $method();
?>

在GO中实现

由于go是静态语言,不能动态载入,只能通过反射来解决:

package main
import (
"fmt"
"log"
"net/http"
"reflect"
"strings"
)
type App struct {
w http.ResponseWriter
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/public/") {
//匹配静态文件服务
} else {
app := &App{w}
rValue := reflect.ValueOf(app)
rType := reflect.TypeOf(app)
path := strings.Split(r.URL.Path, "/")
controlName = path[1]
method, exist := rType.MethodByName(controlName)
if exist {
args := []reflect.Value{rValue}
method.Func.Call(args)
} else {
fmt.Fprintf(w, "method %s not found", r.URL.Path)
}
}
})

log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

//控制器
func (this *App) Say() {
fmt.Fprintf(this.w, "Say called")
}

实际使用时,由于应用与框架不属于同一个包,控制器的reciver肯定不能是框架内的App结构实例(go不能使用别的包的结构来作为reciver):

package controller
import "framework"

type Controller struct{
*framework.App
//controller其它属性
}

func (this *Controller) Say({
//可以直接调用framework.App提供的方法和属性
}

这个时候,http.HandleFunc内的实现方式是:

app := controller.Controller{}
rValue := reflect.ValueOf(app)
rType := reflect.TypeOf(app)
reciver := rValue.Elem().FieldByName("App")
reciver.Set(reflect.ValueOf(&App{w})) //设置controller.Controller的匿名字段App

method, exist := rType.MethodByName(controlName)
if exist {
args := []reflect.Value{rValue}
method.Func.Call(args)
}else{
//404
}

路径分配策略

上述示例代码是直接使用Path的第一段来dispatch,如有需要,可有更多的方式,比如:

根椐固定的请求参数 c=xxx

根椐某个指定Header

根椐Cookie

关于如何支持RESTful,将在下篇讲述。

本篇示例代码只是演示动态路由的实现,没有考虑安全和其它因素
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息