Skip to content

Commit

Permalink
The birth of Gee API Framework ~~~
Browse files Browse the repository at this point in the history
  • Loading branch information
gitlayzer committed Mar 24, 2024
1 parent 8b67154 commit 60fd4cf
Show file tree
Hide file tree
Showing 11 changed files with 490 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
![logo](./img/logo.png)
# Gee API Framework

Gee API Framework encapsulates the net/http framework, making it easier and faster for us to write backend APIs

**Gee has the following characteristics**
- Fast
- Zero allocation router
- Middleware support
- Routes grouping
- Extendable

## Get Started

### Prerequisites

- **[Go](https://go.dev/)**: any one of the **three latest major** [releases](https://go.dev/doc/devel/release) (we test it with these).
91 changes: 91 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package gee

import (
"encoding/json"
"fmt"
"net/http"
)

type H map[string]interface{}

type Context struct {
Writer http.ResponseWriter
Request *http.Request
Path string
Method string
Params map[string]string
StatusCode int
handlers []HandlerFunc
index int
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
return &Context{
Path: req.URL.Path,
Method: req.Method,
Request: req,
Writer: w,
index: -1,
}
}

func (c *Context) Next() {
c.index++
s := len(c.handlers)
for ; c.index < s; c.index++ {
c.handlers[c.index](c)
}
}

func (c *Context) Fail(code int, err string) {
c.index = len(c.handlers)
c.JSON(code, H{"message": err})
}

func (c *Context) Param(key string) string {
value, _ := c.Params[key]
return value
}

func (c *Context) PostForm(key string) string {
return c.Request.FormValue(key)
}

func (c *Context) Query(key string) string {
return c.Request.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
c.StatusCode = code
c.Writer.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
c.SetHeader("Content-Type", "text/plain")
c.Status(code)
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
c.SetHeader("Content-Type", "application/json")
c.Status(code)
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil {
http.Error(c.Writer, err.Error(), 500)
}
}

func (c *Context) Data(code int, data []byte) {
c.Status(code)
c.Writer.Write(data)
}

func (c *Context) HTML(code int, html string) {
c.SetHeader("Content-Type", "text/html")
c.Status(code)
c.Writer.Write([]byte(html))
}
13 changes: 13 additions & 0 deletions debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package gee

import "os"

func IsDebug() bool {
if os.Getenv("GEE_MODE") == "debug" {
return false
} else if os.Getenv("GEE_MODE") == "release" {
return true
} else {
return false
}
}
107 changes: 107 additions & 0 deletions gee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package gee

import (
"fmt"
"log"
"net/http"
"strings"
)

type HandlerFunc func(ctx *Context)

type RouterGroup struct {
prefix string
middlewares []HandlerFunc
parent *RouterGroup
engine *Engine
}

type Engine struct {
*RouterGroup
router *router
groups []*RouterGroup
}

func New() *Engine {
engine := &Engine{router: newRouter()}
engine.RouterGroup = &RouterGroup{engine: engine}
engine.groups = []*RouterGroup{engine.RouterGroup}
return engine
}

func Default() *Engine {
engine := New()
engine.Use(Logger(), Recovery(), Server())
return engine
}

func (group *RouterGroup) Group(prefix string) *RouterGroup {
engine := group.engine
newGroup := &RouterGroup{
prefix: group.prefix + prefix,
parent: group,
engine: engine,
}
engine.groups = append(engine.groups, newGroup)
return newGroup
}

func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
group.middlewares = append(group.middlewares, middlewares...)
}

func (group *RouterGroup) addRoute(method string, comp string, handler HandlerFunc) {
pattern := group.prefix + comp
fmt.Println(Logo)

if !IsDebug() == true {
log.Printf("[Gee] %4s - %s", method, pattern)
group.engine.router.addRoute(method, pattern, handler)
}

group.engine.router.addRoute(method, pattern, handler)
}

func (group *RouterGroup) GET(pattern string, handler HandlerFunc) {
group.addRoute("GET", pattern, handler)
}

func (group *RouterGroup) POST(pattern string, handler HandlerFunc) {
group.addRoute("POST", pattern, handler)
}

func (group *RouterGroup) PUT(pattern string, handler HandlerFunc) {
group.addRoute("PUT", pattern, handler)
}

func (group *RouterGroup) DELETE(pattern string, handler HandlerFunc) {
group.addRoute("DELETE", pattern, handler)
}

func (group *RouterGroup) PATCH(pattern string, handler HandlerFunc) {
group.addRoute("PATCH", pattern, handler)
}

func (group *RouterGroup) HEAD(pattern string, handler HandlerFunc) {
group.addRoute("HEAD", pattern, handler)
}

func (group *RouterGroup) OPTIONS(pattern string, handler HandlerFunc) {
group.addRoute("OPTIONS", pattern, handler)
}

func (engine *Engine) Run(addr string) (err error) {
return http.ListenAndServe(addr, engine)
}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var middlewares []HandlerFunc
for _, group := range engine.groups {
if strings.HasPrefix(r.URL.Path, group.prefix) {
middlewares = append(middlewares, group.middlewares...)
}
}
c := NewContext(w, r)
c.handlers = middlewares
engine.router.handle(c)
}
Binary file added img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gee

import (
"log"
"time"
)

func Logger() HandlerFunc {
return func(c *Context) {
t := time.Now()
c.Next()
log.Printf("[%d] %s in %v", c.StatusCode, c.Request.RequestURI, time.Since(t))
}
}
19 changes: 19 additions & 0 deletions logo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gee

const Logo = `
*** *** ** ** **
***** *** *** ** **
******* *** *** ** **
*** ** *** *** ** **
*** *** ** *** ** **
*** *** ***** ***** ****** ** ***** *******
** ******* ******* ********* ******* ********
** ***** *** ** *** ** **** **** *** ** *** ***
** ***** ********* ********* **** *** ********* ** ***
** *** ********* ********* **** *** ********* ** ***
*** *** ** ** **** *** ** ** ***
*** *** *** *** *** *** **** *** *** *** *** **
******** **** *** **** *** ** *** **** *** **** ***
******** ******* ******* ** *** ******* *******
*** ** *** *** ** *** *** ** ***
`
37 changes: 37 additions & 0 deletions recovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package gee

import (
"fmt"
"log"
"net/http"
"runtime"
"strings"
)

func trace(message string) string {
var pcs [32]uintptr
n := runtime.Callers(3, pcs[:]) // skip first 3 caller

var str strings.Builder
str.WriteString(message + "\nTraceback:")
for _, pc := range pcs[:n] {
fn := runtime.FuncForPC(pc)
file, line := fn.FileLine(pc)
str.WriteString(fmt.Sprintf("\n\t%s:%d", file, line))
}
return str.String()
}

func Recovery() HandlerFunc {
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
message := fmt.Sprintf("%s", err)
log.Printf("%s\n\n", trace(message))
c.Fail(http.StatusInternalServerError, "Internal Server Error")
}
}()

c.Next()
}
}
Loading

0 comments on commit 60fd4cf

Please sign in to comment.