first commit
This commit is contained in:
commit
c132578257
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
}
|
|
@ -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) }
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package framework
|
||||
|
||||
type ControllerHandler func(c *Context) error
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue