编写GO的WEB开发框架(三): 如何进行动态路由 - Go语言中文社区

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


大部份情况下,开发者并不想每写一个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,将在下篇讲述。

本篇示例代码只是演示动态路由的实现,没有考虑安全和其它因素

转载于:https://my.oschina.net/tim8670/blog/630855

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_34351321/article/details/91918519
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-15 20:48:33
  • 阅读 ( 1469 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢