first commit

This commit is contained in:
lulz1 2024-10-16 16:53:09 +08:00
commit c132578257
13 changed files with 597 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/framework1.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/framework1.iml" filepath="$PROJECT_DIR$/.idea/framework1.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

57
controller.go Normal file
View File

@ -0,0 +1,57 @@
package main
import (
"context"
"fmt"
"framework1/framework"
"time"
)
func FooControllerHandler(ctx *framework.Context) error {
finish := make(chan struct{}, 1)
panicChan := make(chan interface{}, 1)
durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), 2*time.Second) // 这里记得当所有事情处理结束后调用 cancel告知 durationCtx 的后续 Context 结束
defer cancel()
go func() {
time.Sleep(1 * time.Second)
ctx.Json(200, map[string]interface{}{"code": 0})
finish <- struct{}{}
}()
select {
case <-finish:
fmt.Println("调用结束了")
case <-durationCtx.Done():
ctx.Json(500, "time out")
case <-panicChan:
ctx.Json(500, "panic")
}
return nil
}
func UserLoginController(c *framework.Context) error {
c.Json(200, "ok UserLoginController")
return nil
}
func SubjectDelController(c *framework.Context) error {
c.Json(200, "ok SubjectDelController")
return nil
}
func SubjectUpdateController(c *framework.Context) error {
c.Json(200, "ok SubjectUpdateController")
return nil
}
func SubjectGetController(c *framework.Context) error {
c.Json(200, "ok SubjectGetController")
return nil
}
func SubjectListController(c *framework.Context) error {
c.Json(200, "ok SubjectListController")
return nil
}

39
framework/IGroup.go Normal file
View File

@ -0,0 +1,39 @@
package framework
type IGroup interface {
Get(string, ControllerHandler)
Post(string, ControllerHandler)
Put(string, ControllerHandler)
Delete(string, ControllerHandler)
}
type Group struct {
core *Core
prefix string
}
func NewGroup(core *Core, prefix string) *Group {
return &Group{core, prefix}
}
func (g *Group) Get(uri string, handler ControllerHandler) {
uri = g.prefix + uri
g.core.Get(uri, handler)
}
func (g *Group) Post(uri string, handler ControllerHandler) {
uri = g.prefix + uri
g.core.POST(uri, handler)
}
func (g *Group) Put(uri string, handler ControllerHandler) {
uri = g.prefix + uri
g.core.PUT(uri, handler)
}
func (g *Group) Delete(uri string, handler ControllerHandler) {
uri = g.prefix + uri
g.core.DELETE(uri, handler)
}
func (c *Core) Group(prefix string) IGroup { return NewGroup(c, prefix) }

147
framework/Tree.go Normal file
View File

@ -0,0 +1,147 @@
package framework
import (
"errors"
"strings"
)
type Tree struct {
root *node // 根节点
}
func NewTree() *Tree {
root := newNode()
return &Tree{root}
}
type node struct {
isLast bool // 代表这个节点是否可以成为最终的路由规则。该节点是否能成为一个独立的uri, 是否自身就是一个终极节点
segment string // uri中的字符串代表这个节点表示的路由中某个段的字符串
handler ControllerHandler // 代表这个节点中包含的控制器,用于最终加载调用
children []*node // 代表这个节点下的子节点
}
func newNode() *node {
return &node{
isLast: false,
segment: "",
children: []*node{},
}
}
func isWildSegment(segment string) bool {
return strings.HasPrefix(segment, ":")
}
func (n *node) filterChildNodes(segment string) []*node {
if len(n.children) == 0 {
return nil
}
if isWildSegment(segment) {
return n.children
}
nodes := make([]*node, 0, len(n.children))
for _, child := range n.children {
if isWildSegment(child.segment) {
nodes = append(nodes, child)
} else if child.segment == segment {
nodes = append(nodes, child)
}
}
return nodes
}
func (n *node) matchNode(uri string) *node {
segments := strings.SplitN(uri, "/", 2) // 使用分隔符将uri切割为两个部分
segment := segments[0] // 第一个部分用于匹配下一层子节点
if segment == "" {
return n.matchNode(segments[1])
}
if !isWildSegment(segment) {
segment = strings.ToUpper(segment)
}
children := n.filterChildNodes(segment) // 匹配符合的下一层子节点
if children == nil || len(children) == 0 {
return nil
}
if len(segments) == 1 {
// 如果segment已经是最后一个节点判断这些children是否有isLast标志
for _, tn := range children {
if tn.isLast {
return tn
}
}
return nil
}
// 如果有2个segment, 递归每个子节点继续进行查找
for _, child := range children {
tnMatch := child.matchNode(segments[1])
if tnMatch != nil {
return tnMatch
}
}
return nil
}
func (tree *Tree) AddRouter(uri string, handler ControllerHandler) error {
n := tree.root
// 确认路由是否冲突
if n.matchNode(uri) != nil {
return errors.New("route exist: " + uri)
}
segments := strings.Split(uri, "/")
for index, segment := range segments {
if segment == "" {
continue
}
if !isWildSegment(segment) {
segment = strings.ToUpper(segment)
}
isLast := index == len(segments)-1
var objNode *node // 标记是否有合适的子节点
children := n.filterChildNodes(segment)
if len(children) > 0 {
// 如果有segment相同的子节点则选择这个子节点
for _, child := range children {
if child.segment == segment {
objNode = child
break
}
}
}
//如果没有找到合适的子节点就要创建一个新节点
if objNode == nil {
child := &node{}
child.segment = segment
if isLast {
child.isLast = true
child.handler = handler
}
n.children = append(n.children, child)
objNode = child
}
n = objNode
}
return nil
}
func (tree *Tree) FindHandler(uri string) ControllerHandler {
match := tree.root.matchNode(uri)
if match == nil {
return nil
}
return match.handler
}

208
framework/context.go Normal file
View File

@ -0,0 +1,208 @@
package framework
import (
"bytes"
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strconv"
"sync"
"time"
)
type Context struct {
request *http.Request
responseWriter http.ResponseWriter
ctx context.Context
handler ControllerHandler
hasTimeout bool // 是否超时标记位
writerMux *sync.Mutex // 写保护机制
}
func NewContext(r *http.Request, w http.ResponseWriter) *Context {
return &Context{
request: r,
responseWriter: w,
ctx: r.Context(),
writerMux: &sync.Mutex{},
}
}
// #region implement context.Context
func (ctx *Context) BaseContext() context.Context {
return ctx.request.Context()
}
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
return ctx.request.Context().Deadline()
}
func (ctx *Context) Done() <-chan struct{} {
return ctx.BaseContext().Done()
}
func (ctx *Context) Err() error {
return ctx.BaseContext().Err()
}
func (ctx *Context) Value(key interface{}) interface{} {
return ctx.BaseContext().Value(key)
}
// Base
func (ctx *Context) WriterMux() *sync.Mutex {
return ctx.writerMux
}
func (ctx *Context) GetRequest() *http.Request {
return ctx.request
}
func (ctx *Context) GetResponse() http.ResponseWriter {
return ctx.responseWriter
}
func (ctx *Context) SetHasTimeout() {
ctx.hasTimeout = true
}
func (ctx *Context) HasTimeout() bool {
return ctx.hasTimeout
}
// #region query url
func (ctx *Context) QueryInt(key string, def int) int {
params := ctx.QueryAll()
if vals, ok := params[key]; ok {
valsLen := len(vals)
if valsLen > 0 {
intVal, err := strconv.Atoi(vals[valsLen-1])
if err != nil {
return def
}
return intVal
}
}
return def
}
func (ctx *Context) QueryString(key string, def string) string {
params := ctx.QueryAll()
if vals, ok := params[key]; ok {
valsLen := len(vals)
if valsLen > 0 {
return vals[valsLen-1]
}
}
return def
}
func (ctx *Context) QueryArray(key string, def []string) []string {
params := ctx.QueryAll()
if vals, ok := params[key]; ok {
return vals
}
return def
}
// 查询所有url后面的参数
func (ctx *Context) QueryAll() map[string][]string {
if ctx.request != nil {
return ctx.request.URL.Query()
}
return map[string][]string{}
}
// #region form post
func (ctx *Context) FormInt(key string, def int) int {
params := ctx.FormAll()
if vals, ok := params[key]; ok {
valsLen := len(vals)
if valsLen > 0 {
intval, err := strconv.Atoi(vals[valsLen-1])
if err != nil {
return def
}
return intval
}
}
return def
}
func (ctx *Context) FormString(key string, def string) string {
params := ctx.FormAll()
if vals, ok := params[key]; ok {
valsLen := len(vals)
if valsLen > 0 {
return vals[valsLen-1]
}
}
return def
}
func (ctx *Context) FormArray(key string, def []string) []string {
params := ctx.FormAll()
if vals, ok := params[key]; ok {
return vals
}
return def
}
func (ctx *Context) FormAll() map[string][]string {
if ctx.request != nil {
return ctx.request.PostForm
}
return map[string][]string{}
}
// #region application/json post
func (ctx *Context) BindJson(obj interface{}) error {
if ctx.request != nil {
body, err := ioutil.ReadAll(ctx.request.Body)
if err != nil {
return err
}
ctx.request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
err = json.Unmarshal(body, obj)
if err != nil {
return err
}
} else {
return errors.New("ctx.request empty")
}
return nil
}
// #endregion
// #region response
func (ctx *Context) Json(status int, obj interface{}) error {
if ctx.HasTimeout() {
return nil
}
ctx.responseWriter.Header().Set("Content-Type", "application/json")
ctx.responseWriter.WriteHeader(status)
byt, err := json.Marshal(obj)
if err != nil {
ctx.responseWriter.WriteHeader(500)
return err
}
_, err = ctx.responseWriter.Write(byt)
if err != nil {
return err
}
return nil
}
func (ctx *Context) HTML(status int, obj interface{}, template string) error {
return nil
}
func (ctx *Context) Text(status int, obj string) error {
return nil
}

3
framework/controller.go Normal file
View File

@ -0,0 +1,3 @@
package framework
type ControllerHandler func(c *Context) error

80
framework/core.go Normal file
View File

@ -0,0 +1,80 @@
package framework
import (
"log"
"net/http"
"strings"
)
type Serve struct {
Addr string
//Handler Hander
}
type Core struct {
router map[string]*Tree
}
func NewCore() *Core {
router := map[string]*Tree{}
router["GET"] = NewTree()
router["POST"] = NewTree()
router["PUT"] = NewTree()
router["DELETE"] = NewTree()
return &Core{router: router}
}
func (c *Core) Get(url string, handler ControllerHandler) {
if err := c.router["GET"].AddRouter(url, handler); err != nil {
log.Fatal("add router error: ", err)
}
}
func (c *Core) POST(url string, handler ControllerHandler) {
if err := c.router["POST"].AddRouter(url, handler); err != nil {
log.Fatal("add router error: ", err)
}
}
func (c *Core) PUT(url string, handler ControllerHandler) {
if err := c.router["PUT"].AddRouter(url, handler); err != nil {
log.Fatal("add router error: ", err)
}
}
func (c *Core) DELETE(url string, handler ControllerHandler) {
if err := c.router["DELETE"].AddRouter(url, handler); err != nil {
log.Fatal("add router error: ", err)
}
}
func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("core.serveHTTP")
ctx := NewContext(r, w)
// 寻找路由
router := c.FindRouteByRequest(r)
if router == nil { // 如果没有找到,这里打印日志
ctx.Json(404, "not found")
return
}
// 调用路由函数如果返回err 代表存在内部错误返回500状态码
if err := router(ctx); err != nil {
ctx.Json(500, "inner error")
return
}
}
func (c *Core) FindRouteByRequest(request *http.Request) ControllerHandler {
uri := request.URL.Path
method := request.Method
upperMethod := strings.ToUpper(method)
upperUri := strings.ToUpper(uri)
if methodHandlers, ok := c.router[upperMethod]; ok {
return methodHandlers.FindHandler(upperUri)
}
return nil
}

1
go.mod Normal file
View File

@ -0,0 +1 @@
module framework1

16
main.go Normal file
View File

@ -0,0 +1,16 @@
package main
import (
"framework1/framework"
"net/http"
)
func main() {
core := framework.NewCore()
registerRouter(core)
server := &http.Server{
Handler: core,
Addr: ":8888",
}
server.ListenAndServe()
}

15
route.go Normal file
View File

@ -0,0 +1,15 @@
package main
import "framework1/framework"
func registerRouter(core *framework.Core) {
core.Get("/user/login", UserLoginController)
subjectGroup := core.Group("/subject")
{
subjectGroup.Delete("/:id", SubjectDelController)
subjectGroup.Put("/:id", SubjectUpdateController)
subjectGroup.Get("/:id", SubjectUpdateController)
subjectGroup.Get("/list/all", SubjectListController)
}
}