Compare commits
7 Commits
63d5d0dc59
...
0ed626e351
Author | SHA1 | Date | |
---|---|---|---|
|
0ed626e351 | ||
|
d90e564620 | ||
|
5cb54ae560 | ||
|
913e33a993 | ||
|
f076ebaa3a | ||
|
33e9d1e613 | ||
|
f804f175e0 |
268
framework/context.go
Normal file
268
framework/context.go
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
package framework
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context type is the customized context of Araneae framework
|
||||||
|
//
|
||||||
|
// It packages the internal context.Context with basic "wr" couple.
|
||||||
|
type Context struct {
|
||||||
|
ctx context.Context
|
||||||
|
request *http.Request
|
||||||
|
responseWriter http.ResponseWriter
|
||||||
|
handler ControllerHandler
|
||||||
|
|
||||||
|
hasTimeout bool
|
||||||
|
writerMux *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext create a new context
|
||||||
|
func NewContext(w http.ResponseWriter, r *http.Request) *Context {
|
||||||
|
return &Context{
|
||||||
|
ctx: r.Context(),
|
||||||
|
request: r,
|
||||||
|
responseWriter: w,
|
||||||
|
writerMux: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {{{ Basic functions
|
||||||
|
|
||||||
|
// WriterMux returns the writer mutex.
|
||||||
|
//
|
||||||
|
// This is useful when goroutines concurrently write into responseWriter,
|
||||||
|
// while at the same time we are writing into the responseWriter for a
|
||||||
|
// panic or timeout.
|
||||||
|
// We can protect it at the context level.
|
||||||
|
func (ctx *Context) WriterMux() *sync.Mutex {
|
||||||
|
return ctx.writerMux
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequest returns the original request
|
||||||
|
func (ctx *Context) GetRequest() *http.Request {
|
||||||
|
return ctx.request
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResponseWriter returns the original response writer
|
||||||
|
func (ctx *Context) GetResponseWriter() http.ResponseWriter {
|
||||||
|
return ctx.responseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHasTimeout indicates that the context has timeout.
|
||||||
|
//
|
||||||
|
// So that other goroutines won't write into the responseWriter anymore
|
||||||
|
func (ctx *Context) SetHasTimeout() {
|
||||||
|
ctx.hasTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTimeout returns whether the context has timeout.
|
||||||
|
func (ctx *Context) HasTimeout() bool {
|
||||||
|
return ctx.hasTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
// {{{ Implements context interface
|
||||||
|
|
||||||
|
// BaseContext return a request default Context
|
||||||
|
func (ctx *Context) BaseContext() context.Context {
|
||||||
|
return ctx.request.Context()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done calls the base function
|
||||||
|
func (ctx *Context) Done() <-chan struct{} {
|
||||||
|
return ctx.BaseContext().Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deadline calls the base function
|
||||||
|
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
return ctx.BaseContext().Deadline()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err calls the base function
|
||||||
|
func (ctx *Context) Err() error {
|
||||||
|
return ctx.BaseContext().Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value calls the base function
|
||||||
|
func (ctx *Context) Value(key any) any {
|
||||||
|
return ctx.BaseContext().Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
// {{{ Implements request functions
|
||||||
|
|
||||||
|
// {{{ Request URI
|
||||||
|
|
||||||
|
// QueryInt gets an int value from the query request
|
||||||
|
func (ctx *Context) QueryInt(key string, defval int) (int, error) {
|
||||||
|
params, err := ctx.QueryAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
if vals, ok := params[key]; ok {
|
||||||
|
len := len(vals)
|
||||||
|
if len > 0 {
|
||||||
|
intval, err := strconv.Atoi(vals[len-1]) // return the last elem
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
return intval, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryString gets a string value from the query request
|
||||||
|
func (ctx *Context) QueryString(key string, defval string) (string, error) {
|
||||||
|
params, err := ctx.QueryAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
if vals, ok := params[key]; ok {
|
||||||
|
len := len(vals)
|
||||||
|
if len > 0 {
|
||||||
|
return vals[len-1], nil // return the last elem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryArray gets an array of string values from the query request
|
||||||
|
func (ctx *Context) QueryArray(key string, defval []string) ([]string, error) {
|
||||||
|
params, err := ctx.QueryAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
if vals, ok := params[key]; ok {
|
||||||
|
return vals, nil // return the last elem
|
||||||
|
}
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryAll returns all queries in a request URL
|
||||||
|
func (ctx *Context) QueryAll() (url.Values, error) {
|
||||||
|
if ctx.request != nil {
|
||||||
|
return map[string][]string(ctx.request.URL.Query()), nil
|
||||||
|
}
|
||||||
|
return url.Values{}, errors.New("missing request in the context")
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
// {{{ Post form
|
||||||
|
|
||||||
|
// FormInt gets an int value from the submitted form
|
||||||
|
func (ctx *Context) FormInt(key string, defval int) (int, error) {
|
||||||
|
vals, err := ctx.FormAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
valStrs, ok := vals[key]
|
||||||
|
if !ok {
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
valInt, err := strconv.Atoi(valStrs[0]) // Get the first one as result
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return valInt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormString gets a string value from the submitted form
|
||||||
|
func (ctx *Context) FormString(key string, defval string) (string, error) {
|
||||||
|
vals, err := ctx.FormAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
valStrs, ok := vals[key]
|
||||||
|
if !ok {
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return valStrs[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormArray gets an array of string values from the submitted form
|
||||||
|
func (ctx *Context) FormArray(key string, defval []string) ([]string, error) {
|
||||||
|
vals, err := ctx.FormAll()
|
||||||
|
if err != nil {
|
||||||
|
return defval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
valStrs, ok := vals[key]
|
||||||
|
if !ok {
|
||||||
|
return defval, errors.New("key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return valStrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormAll gets everything from the submitted form
|
||||||
|
func (ctx *Context) FormAll() (url.Values, error) {
|
||||||
|
if ctx.request != nil {
|
||||||
|
err := ctx.request.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return url.Values{}, err
|
||||||
|
}
|
||||||
|
return ctx.request.PostForm, err
|
||||||
|
}
|
||||||
|
return url.Values{}, errors.New("missing request in the context")
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
// {{{ application/json
|
||||||
|
|
||||||
|
// ReadJSON binds the request JSON body to an object.
|
||||||
|
//
|
||||||
|
// A pointer of obj should be passed.
|
||||||
|
func (ctx *Context) ReadJSON(obj any) error {
|
||||||
|
if ctx.request == nil {
|
||||||
|
return errors.New("missing request in the context")
|
||||||
|
}
|
||||||
|
dec := json.NewDecoder(ctx.request.Body)
|
||||||
|
err := dec.Decode(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
return errors.New("body must have only a single JSON value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteJSON send back an object in JSON format with the status code
|
||||||
|
func (ctx *Context) WriteJSON(status int, obj any) error {
|
||||||
|
// There is a timeout, some error message data must have already been
|
||||||
|
// written to the output. Stop writing anything into the responseWriter.
|
||||||
|
if ctx.HasTimeout() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.responseWriter.Header().Set("Content-type", "application/json")
|
||||||
|
ctx.responseWriter.WriteHeader(status)
|
||||||
|
_, err = ctx.responseWriter.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
// }}}
|
3
framework/controller.go
Normal file
3
framework/controller.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package framework
|
||||||
|
|
||||||
|
type ControllerHandler func(c *Context) error
|
@ -1,18 +1,106 @@
|
|||||||
package framework
|
package framework
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Core is the core struct of the framework
|
// Core is the core struct of the framework
|
||||||
type Core struct{}
|
type Core struct {
|
||||||
|
router map[string]*Trie
|
||||||
|
}
|
||||||
|
|
||||||
// NewCore initialize the Core.
|
// NewCore initialize the Core.
|
||||||
func NewCore() *Core {
|
func NewCore() *Core {
|
||||||
return &Core{}
|
getRouter := NewTrie()
|
||||||
|
postRouter := NewTrie()
|
||||||
|
putRouter := NewTrie()
|
||||||
|
deleteRouter := NewTrie()
|
||||||
|
|
||||||
|
router := map[string]*Trie{}
|
||||||
|
router["GET"] = getRouter
|
||||||
|
router["POST"] = postRouter
|
||||||
|
router["PUT"] = putRouter
|
||||||
|
router["DELETE"] = deleteRouter
|
||||||
|
|
||||||
|
return &Core{router: router}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is a simple get router
|
||||||
|
func (c *Core) Get(url string, handler ControllerHandler) {
|
||||||
|
upperUrl := strings.ToUpper(url)
|
||||||
|
if err := c.router["GET"].AddRouter(upperUrl, handler); err != nil{
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post is a simple post router
|
||||||
|
func (c *Core) Post(url string, handler ControllerHandler) {
|
||||||
|
upperUrl := strings.ToUpper(url)
|
||||||
|
if err := c.router["POST"].AddRouter(upperUrl, handler); err != nil{
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put is a simple put router
|
||||||
|
func (c *Core) Put(url string, handler ControllerHandler) {
|
||||||
|
upperUrl := strings.ToUpper(url)
|
||||||
|
if err := c.router["PUT"].AddRouter(upperUrl, handler); err != nil{
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is a simple delete router
|
||||||
|
func (c *Core) Delete(url string, handler ControllerHandler) {
|
||||||
|
upperUrl := strings.ToUpper(url)
|
||||||
|
if err := c.router["DELETE"].AddRouter(upperUrl, handler); err != nil{
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRouteByRequest finds route using the request
|
||||||
|
func (c *Core) FindRouteByRequest(r *http.Request) ControllerHandler {
|
||||||
|
upperUri := strings.ToUpper(r.URL.Path)
|
||||||
|
upperMethod := strings.ToUpper(r.Method)
|
||||||
|
|
||||||
|
mapper, ok := c.router[upperMethod]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Method %q is not recognized\n", upperMethod)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
controller := mapper.FindRoute(upperUri)
|
||||||
|
if controller == nil {
|
||||||
|
log.Printf("URI %q is not recognized\n", r.URL.Path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) Group(prefix string) IGroup {
|
||||||
|
return &Group{
|
||||||
|
core: c,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the Handler interface
|
// ServeHTTP implements the Handler interface
|
||||||
func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO
|
log.Println("Welcome to the Araneae framework")
|
||||||
|
|
||||||
|
ctx := NewContext(w, r)
|
||||||
|
|
||||||
|
router := c.FindRouteByRequest(r)
|
||||||
|
if router == nil {
|
||||||
|
ctx.WriteJSON(http.StatusNotFound, "Request not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := router(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.WriteJSON(http.StatusInternalServerError, "Internal error")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
framework/group.go
Normal file
43
framework/group.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package framework
|
||||||
|
|
||||||
|
// IGroup prefix routes
|
||||||
|
type IGroup interface {
|
||||||
|
Get(string, ControllerHandler)
|
||||||
|
Post(string, ControllerHandler)
|
||||||
|
Put(string, ControllerHandler)
|
||||||
|
Delete(string, ControllerHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group is the implementation of IGroup interface
|
||||||
|
type Group struct {
|
||||||
|
core *Core
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGroup create a new prefix group
|
||||||
|
func NewGroup(core *Core, prefix string) *Group {
|
||||||
|
return &Group{
|
||||||
|
core: core,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is a simple get router of the group
|
||||||
|
func (g *Group) Get(url string, handler ControllerHandler) {
|
||||||
|
g.core.Get(g.prefix+url, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post is a simple post router of the group
|
||||||
|
func (g *Group) Post(url string, handler ControllerHandler) {
|
||||||
|
g.core.Post(g.prefix+url, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put is a simple put router of the group
|
||||||
|
func (g *Group) Put(url string, handler ControllerHandler) {
|
||||||
|
g.core.Put(g.prefix+url, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is a simple delete router of the group
|
||||||
|
func (g *Group) Delete(url string, handler ControllerHandler) {
|
||||||
|
g.core.Delete(g.prefix+url, handler)
|
||||||
|
}
|
154
framework/trie.go
Normal file
154
framework/trie.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package framework
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Trie struct {
|
||||||
|
root *node
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrie() *Trie {
|
||||||
|
return &Trie{root: newNode("")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trie) FindRoute(uri string) ControllerHandler {
|
||||||
|
uri = strings.TrimPrefix(uri, "/")
|
||||||
|
if uri == "" {
|
||||||
|
return t.root.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
found := t.root.findRoute(uri)
|
||||||
|
if found == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return found.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trie) AddRouter(uri string, handler ControllerHandler) error {
|
||||||
|
uri = strings.TrimPrefix(uri, "/")
|
||||||
|
if uri == "" {
|
||||||
|
t.root.isLast = true
|
||||||
|
t.root.handler = handler
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
upperUri := strings.ToUpper(uri)
|
||||||
|
match := t.FindRoute(upperUri)
|
||||||
|
if match != nil {
|
||||||
|
// existing route
|
||||||
|
return fmt.Errorf("existing route for %q", uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The route does not exist, add it to the tree
|
||||||
|
err := t.root.addRoute(upperUri, handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
isLast bool
|
||||||
|
segment string
|
||||||
|
handler ControllerHandler
|
||||||
|
children []*node
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNode(segment string) *node {
|
||||||
|
return &node{segment: segment}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWildcard(s string) bool {
|
||||||
|
return strings.HasPrefix(s, ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
// /user/name
|
||||||
|
// /user/:id/name
|
||||||
|
// /user/3/name
|
||||||
|
|
||||||
|
// findRoute finds the handler for the uri if exists.
|
||||||
|
//
|
||||||
|
// we suppose that uri passed here doesn't begin with "/"
|
||||||
|
func (n *node) findRoute(uri string) *node {
|
||||||
|
splitted := strings.SplitN(uri, "/", 2)
|
||||||
|
splittedLen := len(splitted)
|
||||||
|
|
||||||
|
if isWildcard(splitted[0]) {
|
||||||
|
// input is a wildcard, check if this endpoint has already children
|
||||||
|
// if so, return the first one which is not nil.
|
||||||
|
nbChildren := len(n.children)
|
||||||
|
if nbChildren > 0 && n.children[0].segment != splitted[0] {
|
||||||
|
// several nodes exist, return the first one that is not nil
|
||||||
|
return n.children[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find the value in childre
|
||||||
|
for _, child := range n.children {
|
||||||
|
if isWildcard(child.segment) || child.segment == splitted[0] {
|
||||||
|
if splittedLen == 1 {
|
||||||
|
// This is the last value, do the check and return
|
||||||
|
if child.isLast {
|
||||||
|
// if isLast, that means we have already registered the endpoint
|
||||||
|
return child
|
||||||
|
} else {
|
||||||
|
// otherwise, take it as not registered
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// More segments to check
|
||||||
|
return child.findRoute(splitted[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing found in the children
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) addRoute(uri string, handler ControllerHandler) error {
|
||||||
|
splitted := strings.SplitN(uri, "/", 2)
|
||||||
|
splittedLen := len(splitted)
|
||||||
|
isLast := splittedLen == 1
|
||||||
|
|
||||||
|
// try to find the value in childre
|
||||||
|
for _, child := range n.children {
|
||||||
|
if isWildcard(child.segment) || child.segment == splitted[0] {
|
||||||
|
if isLast {
|
||||||
|
// This is the last value, do the check and return
|
||||||
|
if child.isLast {
|
||||||
|
// if isLast, that means we have already registered the endpoint
|
||||||
|
return errors.New("node exists")
|
||||||
|
} else {
|
||||||
|
// otherwise, set the child
|
||||||
|
child.isLast = true
|
||||||
|
child.handler = handler
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// More segments to check
|
||||||
|
return child.addRoute(splitted[1], handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new node
|
||||||
|
new := newNode(splitted[0])
|
||||||
|
if isLast {
|
||||||
|
// this is the end
|
||||||
|
new.handler = handler
|
||||||
|
new.isLast = true
|
||||||
|
n.children = append(n.children, new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// continue
|
||||||
|
new.isLast = false
|
||||||
|
err := new.addRoute(splitted[1], handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n.children = append(n.children, new)
|
||||||
|
return nil
|
||||||
|
}
|
83
handlers.go
Normal file
83
handlers.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.vinchent.xyz/vinchent/go-web/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
// func FooControllerHandler(ctx *framework.Context) error {
|
||||||
|
// return ctx.WriteJSON(http.StatusOK, map[string]any{
|
||||||
|
// "code": 0,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
func FooControllerHandler(ctx *framework.Context) error {
|
||||||
|
durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), time.Duration(1*time.Second))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
finish := make(chan struct{}, 1)
|
||||||
|
panicChan := make(chan interface{}, 1)
|
||||||
|
|
||||||
|
// some long task
|
||||||
|
go func() {
|
||||||
|
// Deal with the panic during the work
|
||||||
|
defer func() {
|
||||||
|
if p := recover(); p != nil {
|
||||||
|
panicChan <- p
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// do the business
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
ctx.WriteJSON(http.StatusOK, "ok")
|
||||||
|
finish <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-panicChan:
|
||||||
|
// Protect ResponseWriter for concurrently writing from different
|
||||||
|
// goroutines if there are any
|
||||||
|
ctx.WriterMux().Lock()
|
||||||
|
defer ctx.WriterMux().Unlock()
|
||||||
|
log.Println("panicked")
|
||||||
|
ctx.WriteJSON(http.StatusInternalServerError, "panicked")
|
||||||
|
case <-finish:
|
||||||
|
log.Println("finished")
|
||||||
|
case <-durationCtx.Done():
|
||||||
|
ctx.WriterMux().Lock()
|
||||||
|
defer ctx.WriterMux().Unlock()
|
||||||
|
log.Println("Timeout")
|
||||||
|
ctx.WriteJSON(http.StatusInternalServerError, "time out")
|
||||||
|
ctx.SetHasTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserLoginController(ctx *framework.Context) error {
|
||||||
|
ctx.WriteJSON(http.StatusOK, "ok")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubjectDelController(ctx *framework.Context) error {
|
||||||
|
ctx.WriteJSON(http.StatusAccepted, "deleted")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubjectUpdateController(ctx *framework.Context) error {
|
||||||
|
ctx.WriteJSON(http.StatusAccepted, "updated")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubjectGetController(ctx *framework.Context) error {
|
||||||
|
ctx.WriteJSON(http.StatusAccepted, "got")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubjectListController(ctx *framework.Context) error {
|
||||||
|
ctx.WriteJSON(http.StatusAccepted, "list")
|
||||||
|
return nil
|
||||||
|
}
|
12
main.go
12
main.go
@ -1,24 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.vinchent.xyz/vinchent/go-web/framework"
|
"git.vinchent.xyz/vinchent/go-web/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FooHandler struct{}
|
|
||||||
|
|
||||||
func (foo FooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
core := framework.NewCore()
|
||||||
|
registerRouter(core)
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: ":8080",
|
Addr: ":8080",
|
||||||
Handler: framework.NewCore(),
|
Handler: core,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.ListenAndServe(); err != nil {
|
if err := server.ListenAndServe(); err != nil {
|
||||||
|
15
routes.go
Normal file
15
routes.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "git.vinchent.xyz/vinchent/go-web/framework"
|
||||||
|
|
||||||
|
func registerRouter(core *framework.Core) {
|
||||||
|
core.Get("/user/login", UserLoginController)
|
||||||
|
|
||||||
|
subjectApi := core.Group("/subject")
|
||||||
|
{
|
||||||
|
subjectApi.Delete("/:id", SubjectDelController)
|
||||||
|
subjectApi.Put("/:id", SubjectUpdateController)
|
||||||
|
subjectApi.Get("/:id", SubjectGetController)
|
||||||
|
subjectApi.Get("/list/all", SubjectListController)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user