Compare commits

...

35 Commits

Author SHA1 Message Date
8aceb5ff8c fix crash 2024-09-30 19:44:10 +02:00
9da3012789 Integrate cobra into the framework part1 2024-09-30 19:38:16 +02:00
fc4f94e967 GoWeb: add cobra 2024-09-30 14:02:36 +02:00
7c4a95ac04 Repo: Add binary to gitignore 2024-09-30 13:53:00 +02:00
17b19e3a10 Repo: reorganize the repo structure 2024-09-30 13:42:37 +02:00
c3fc59b8f1 Gin: add container into Gin 2024-09-30 09:32:33 +02:00
f0fc7ee2cb Gin: add gin into the framework 2024-09-29 23:50:00 +02:00
f0b688ff8c Container: Add Gin web service provider 2024-09-28 22:54:00 +02:00
f866f18cfd Container: create service container and service provider 2024-09-28 22:54:00 +02:00
722e8191a9 GoWeb: Remove everything to use Gin 2024-09-28 22:53:41 +02:00
c5ab1debdb stop: use graceful shutdown for 5s 2024-09-26 23:05:05 +02:00
c0fea38790 stop: use graceful shutdown 2024-09-26 22:58:55 +02:00
b98513ad36 stop: catch signals 2024-09-26 22:27:26 +02:00
0786a97a77 context: Get params from url 2024-09-26 21:15:15 +02:00
cb1ea9f701 request/response: add implementations for response writer 2024-09-26 09:32:44 +02:00
8f4b378fdd request/response: add more implementations 2024-09-25 22:45:25 +02:00
099d1aeb0f request/response: add interfaces and implementations 2024-09-25 22:20:00 +02:00
3b16d6b16a middleware: refactor tests 2024-09-25 14:09:29 +02:00
fc4103cff3 middleware: add tests 2024-09-25 13:52:12 +02:00
e770731643 middleware: Add recovery middleware 2024-09-25 13:35:38 +02:00
3bf14b9c04 middleware: Integrate middlewares into router functions 2024-09-25 13:31:37 +02:00
1aa9b78bdc context: implement handler chain 2024-09-24 23:33:33 +02:00
e8c4cbaa89 middleware/timeout: implement timeout middleware 2024-09-24 23:21:48 +02:00
14af16c71e middleware/timeout: add timeout middleware 2024-09-24 23:07:42 +02:00
9f83bc7b58 go-sum: commit go sum 2024-09-24 23:07:13 +02:00
0a5e329aa4 core: format 2024-09-24 23:06:39 +02:00
0ed626e351 Route: Use a trie to register routes 2024-09-16 21:04:18 +02:00
d90e564620 Router: add prefix group interface and implementation 2024-09-16 12:05:35 +02:00
5cb54ae560 Router: add static routers 2024-09-16 11:39:38 +02:00
913e33a993 Context: Test context with timeout 2024-09-16 11:13:40 +02:00
f076ebaa3a Context: hard foo controller mapping for test purpose 2024-09-16 11:13:40 +02:00
33e9d1e613 Context: use timeout in JSON writer 2024-09-16 11:13:40 +02:00
f804f175e0 Context: create a customized Context type.
To package the internal context and other common structs, especially
http.Request and http.ResponseWriter.

Provides helper functìons.
2024-09-15 21:38:31 +02:00
63d5d0dc59 Add framework core type 2024-09-05 17:30:59 +02:00
b96656a469 Official doc for http server 2024-09-05 16:23:58 +02:00
28 changed files with 1047 additions and 0 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@
# Go workspace file
go.work
go-web

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "framework/gin"]
path = framework/gin
url = gitea@git.vinchent.xyz:vinchent/gin.git
[submodule "framework/cobra"]
path = framework/cobra
url = gitea@git.vinchent.xyz:vinchent/cobra.git

30
app/console/kernel.go Normal file
View File

@ -0,0 +1,30 @@
package console
import (
"git.vinchent.xyz/vinchent/go-web/framework"
"git.vinchent.xyz/vinchent/go-web/framework/commands"
"github.com/spf13/cobra"
)
func RunCommand(container framework.Container) error {
rootCmd := &cobra.Command{
Use: "goweb",
Short: "goweb command",
Long: "the command line tool for goweb framework.",
RunE: func(cmd *cobra.Command, args []string) error {
cmd.InitDefaultHelpFlag()
return cmd.Help()
},
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
}
rootCmd.SetContainer(container)
commands.AddKernelCommands(rootCmd)
AddAppCommand(rootCmd)
return rootCmd.Execute()
}
func AddAppCommand(rootCmd *cobra.Command) {
// rootCmd.AddCommand(demo.InitFoo())
}

14
app/web/kernel.go Normal file
View File

@ -0,0 +1,14 @@
package web
import "github.com/gin-gonic/gin"
func NewHttpEngine() (*gin.Engine, error) {
// Use ReleaseMode by default to not ouput debug info
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
Routes(r)
return r, nil
}

View File

9
app/web/route.go Normal file
View File

@ -0,0 +1,9 @@
package web
import "github.com/gin-gonic/gin"
func Routes(r *gin.Engine) {
r.Static("/dist/", "./dist/")
// demo.Register(r)
}

1
framework/cobra Submodule

Submodule framework/cobra added at 6d888d3313

View File

@ -0,0 +1,8 @@
package commands
import "github.com/spf13/cobra"
func AddKernelCommands(root *cobra.Command) {
// root.AddCommand(DemoCommand)
// root.AddCommand(initAppCommand())
}

189
framework/container.go Normal file
View File

@ -0,0 +1,189 @@
package framework
import (
"fmt"
"io"
"sync"
)
type Container interface {
// Bind binds a service provider to the container.
//
// If the service exists, the old one will be replaced by the new one.
// No error is returned in this case.
//
// Return any Init() error.
Bind(provider ServiceProvider) error
// IsBound returns true the provider is bound to the container.
IsBound(name string) bool
// Make gets or creates a service by its name.
//
// Return error if the service cannot be initiated or doesn't exist.
Make(name string) (interface{}, error)
// MustMake makes a service, supposing that it is not instantiated before.
// Return nil if error.
MustMake(name string) interface{}
// MakeNew creates a new instance of the service with different parameters.
//
// Used for non singleton services.
MakeNew(name string, params []interface{}) (interface{}, error)
}
type GoWebContainer struct {
// Must implement Container interfaces.
Container
// Service providers by name.
providers map[string]ServiceProvider
// Instantiated services by name.
instances map[string]interface{}
// RWMutex to protect the changes in container.
mu sync.RWMutex
}
func errUnknownService(name string) error {
return fmt.Errorf("service %q is not registered in the container", name)
}
func NewGoWebContainer() *GoWebContainer {
return &GoWebContainer{
providers: map[string]ServiceProvider{},
instances: map[string]interface{}{},
mu: sync.RWMutex{},
}
}
func (c *GoWebContainer) PrintProviders(w io.Writer) {
ret := make([]string, len(c.providers))
for _, provider := range c.providers {
name := provider.Name()
ret = append(ret, name)
}
fmt.Fprintf(w, "%v", ret)
}
func (c *GoWebContainer) Bind(provider ServiceProvider) error {
c.bindProvider(provider)
// Instantiate Later ?
if provider.InstantiateLater() {
return nil
}
// Instantiate now !
_, err := c.instantiate(provider, nil)
if err != nil {
return err
}
return nil
}
func (c *GoWebContainer) bindProvider(provider ServiceProvider) {
c.mu.Lock()
defer c.mu.Unlock()
name := provider.Name()
c.providers[name] = provider
}
func (c *GoWebContainer) IsBound(name string) bool {
return c.getServiceProvider(name) != nil
}
func (c *GoWebContainer) getServiceProvider(name string) ServiceProvider {
c.mu.RLock()
defer c.mu.RUnlock()
provider, ok := c.providers[name]
if ok {
return provider
}
return nil
}
func (c *GoWebContainer) Make(name string) (interface{}, error) {
return c.makeServiceInstance(name, nil, false)
}
func (c *GoWebContainer) MustMake(name string) interface{} {
ins, err := c.makeServiceInstance(name, nil, false)
if err != nil {
return nil
}
return ins
}
func (c *GoWebContainer) MakeNew(name string, params []interface{}) (interface{}, error) {
return c.makeServiceInstance(name, params, true)
}
func (c *GoWebContainer) getServiceInstance(name string) interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
instance, ok := c.instances[name]
if ok {
return instance
}
return nil
}
func (c *GoWebContainer) makeServiceInstance(
name string,
params []interface{},
forceNew bool,
) (interface{}, error) {
provider := c.getServiceProvider(name)
if provider == nil {
return nil, errUnknownService(name)
}
if forceNew {
return c.instantiate(provider, params)
}
instance := c.getServiceInstance(name)
if instance != nil {
return instance, nil
}
// create a new instance
return c.instantiate(provider, params)
}
// instantiate instantiates a new instance.
// If no params are provided, then default params are used.
func (c *GoWebContainer) instantiate(
provider ServiceProvider,
params []interface{},
) (interface{}, error) {
c.mu.Lock()
defer c.mu.Unlock()
if err := provider.Init(c); err != nil {
// TODO: Error should be wrapped.
return nil, err
}
if params == nil {
params = provider.Params(c)
}
construct := provider.Register(c)
instance, err := construct(params...)
if err != nil {
// TODO: Error should be wrapped.
return nil, err
}
c.instances[provider.Name()] = instance
return instance, nil
}

View File

@ -0,0 +1,88 @@
package framework
import (
"log"
"reflect"
"testing"
)
// {{{ contract
const DummyProviderName = "container:test"
type IDummyService interface {
GetDummy() DummyStruct
}
type DummyStruct struct {
Name string
}
// }}}
// {{{ provider
type DummyServiceProvider struct{}
func (p *DummyServiceProvider) Name() string {
return DummyProviderName
}
func (p *DummyServiceProvider) Register(c Container) NewInstance {
return NewDummyService
}
func (p *DummyServiceProvider) Init(c Container) error {
log.Println("init")
return nil
}
func (p *DummyServiceProvider) InstantiateLater() bool {
return true
}
func (p *DummyServiceProvider) Params(c Container) []interface{} {
return []interface{}{c}
}
// }}}
// {{{ service
type DummyService struct {
IDummyService
// parameters
c Container
}
func (d *DummyService) GetDummy() DummyStruct {
return DummyStruct{
Name: "Dummy!",
}
}
func NewDummyService(params ...interface{}) (interface{}, error) {
c := params[0].(Container)
log.Println("new dummy service")
return &DummyService{c: c}, nil
}
// }}}
func TestBind(t *testing.T) {
container := NewGoWebContainer()
provider := &DummyServiceProvider{}
container.Bind(provider)
dummyService := container.MustMake(DummyProviderName).(IDummyService)
want := DummyStruct{
Name: "Dummy!",
}
got := dummyService.GetDummy()
if !reflect.DeepEqual(want, got) {
t.Errorf("want %v, got %v ", want, got)
}
}

15
framework/contract/app.go Normal file
View File

@ -0,0 +1,15 @@
package contract
const AppName = "goweb:app"
type App interface {
Version() string
BaseFolder() string
ConfigFolder() string
LogFolder() string
ProvidersFolder() string
MiddlewaresFolder() string
CommandsFolder() string
RuntimeFolder() string
TestsFolder() string
}

View File

@ -0,0 +1,10 @@
package contract
import "net/http"
const KernelName = "goweb:kernel"
type Kernel interface {
// HttpEngine we use actually gin.Engine, but it is open to change.
HttpEngine() http.Handler
}

1
framework/gin Submodule

Submodule framework/gin added at a5849d15be

View File

@ -0,0 +1,20 @@
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Recovery is a middleware that recovers from the panic
func Recovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.JSON(http.StatusInternalServerError, err)
}
}()
c.Next()
}
}

View File

@ -0,0 +1,31 @@
package middleware
import (
"log"
"github.com/gin-gonic/gin"
)
func Test1() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test1 pre")
c.Next()
log.Println("middleware test1 post")
}
}
func Test2() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test2 pre")
c.Next()
log.Println("middleware test2 post")
}
}
func Test3() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test3 pre")
c.Next()
log.Println("middleware test3 post")
}
}

View File

@ -0,0 +1,46 @@
package middleware
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func Timeout(d time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
finish := make(chan struct{}, 1)
panicChan := make(chan interface{}, 1)
durationCtx, cancel := context.WithTimeout(c.Request.Context(), d)
defer cancel()
go func() {
// Handle panic
defer func() {
if p := recover(); p != nil {
panicChan <- p
}
}()
// Run the next middleware or the business logic
c.Next()
finish <- struct{}{}
}()
select {
case p := <-panicChan:
// panic
log.Println(p)
c.Status(http.StatusInternalServerError)
case <-finish:
// finish normally
log.Println("finish")
case <-durationCtx.Done():
c.JSON(http.StatusRequestTimeout, "time out")
}
}
}

View File

@ -0,0 +1,98 @@
package middleware
import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
)
func TestTimeout(t *testing.T) {
t.Run("Test timeout handler", func(t *testing.T) {
timeoutHandler := Timeout(1 * time.Millisecond)
longHandler := func(c *gin.Context) {
time.Sleep(2 * time.Millisecond)
}
res := prepareMiddlewareTest(t, timeoutHandler, longHandler)
assertCode(t, res.StatusCode, http.StatusRequestTimeout)
assertBody(t, res.Body, "\"time out\"")
})
t.Run("Test no timeout", func(t *testing.T) {
timeoutHandler := Timeout(2 * time.Millisecond)
quickHandler := func(c *gin.Context) {
// time.Sleep(1 * time.Millisecond)
c.JSON(http.StatusOK, "ok")
}
res := prepareMiddlewareTest(t, timeoutHandler, quickHandler)
assertCode(t, res.StatusCode, http.StatusOK)
})
}
func TestRecover(t *testing.T) {
t.Run("Test panic", func(t *testing.T) {
recoverer := Recovery()
panicHandler := func(c *gin.Context) {
panic("panic")
}
res := prepareMiddlewareTest(t, recoverer, panicHandler)
assertCode(t, res.StatusCode, http.StatusInternalServerError)
})
t.Run("Test no panic", func(t *testing.T) {
recoverer := Recovery()
normalHandler := func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
}
res := prepareMiddlewareTest(t, recoverer, normalHandler)
assertCode(t, res.StatusCode, http.StatusOK)
})
}
func prepareMiddlewareTest(
t testing.TB,
mid gin.HandlerFunc,
in gin.HandlerFunc,
) *http.Response {
t.Helper()
request := httptest.NewRequest(http.MethodGet, "/", nil)
response := httptest.NewRecorder()
_, r := gin.CreateTestContext(response)
r.Use(mid)
r.GET("/", in)
r.ServeHTTP(response, request)
res := response.Result()
return res
}
func assertCode(t testing.TB, got int, want int) {
t.Helper()
if got != want {
t.Errorf("status code got %d, want %d", got, want)
}
}
func assertBody(t testing.TB, got io.Reader, want string) {
t.Helper()
buf, _ := io.ReadAll(got)
if cmp := bytes.Compare(buf, []byte(want)); cmp != 0 {
t.Errorf("got %q, want %q", string(buf), want)
}
}

24
framework/provider.go Normal file
View File

@ -0,0 +1,24 @@
package framework
// NewInstance defines the function to create a new instance for a service.
type NewInstance func(...interface{}) (interface{}, error)
// ServiceProvider is the interface for a service provider
type ServiceProvider interface {
// Register a service into the service container, return a NewInstance constructer
Register(Container) NewInstance
// Init is called when instantiating the service.
Init(Container) error
// InstantiateLater decides if the service is instantiated at register phase.
// If true, then this service shall be instantiated later.
InstantiateLater() bool
// Params defines the parameters needed to instantiate a service.
// NOTE: First one should always be the container.
Params(Container) []interface{}
// Name is the name of the service provider
Name() string
}

View File

@ -0,0 +1,30 @@
package app
import (
"git.vinchent.xyz/vinchent/go-web/framework"
"git.vinchent.xyz/vinchent/go-web/framework/contract"
)
type GoWebAppProvider struct {
BaseFolder string
}
func (goweb *GoWebAppProvider) Register(c framework.Container) framework.NewInstance {
return NewGoWebApp
}
func (goweb *GoWebAppProvider) Init(c framework.Container) error {
return nil
}
func (goweb *GoWebAppProvider) InstantiateLater() bool {
return false
}
func (goweb *GoWebAppProvider) Params(c framework.Container) []interface{} {
return []interface{}{c, goweb.BaseFolder}
}
func (goweb *GoWebAppProvider) Name() string {
return contract.AppName
}

View File

@ -0,0 +1,102 @@
package app
import (
"flag"
"fmt"
"path/filepath"
"git.vinchent.xyz/vinchent/go-web/framework"
"git.vinchent.xyz/vinchent/go-web/framework/utils"
)
type GoWebApp struct {
container framework.Container
baseFolder string
}
const AppVersion = "0.0.1"
func ErrParamsAmount(want int, got int) error {
return fmt.Errorf("want %d arguments, got %d", want, got)
}
// NewGoWebApp creates a new GoWeb app.
//
// \params[in]: container
// \params[in]: baseFolder
func NewGoWebApp(params ...interface{}) (interface{}, error) {
if len(params) != 2 {
return nil, ErrParamsAmount(2, len(params))
}
container := params[0].(framework.Container)
baseFolder := params[1].(string)
return &GoWebApp{
container: container,
baseFolder: baseFolder,
}, nil
}
func (goweb *GoWebApp) Version() string {
return AppVersion
}
func (goweb *GoWebApp) BaseFolder() string {
if goweb.baseFolder != "" {
return goweb.baseFolder
}
// not defined yet
var baseFolder string
flag.StringVar(
&baseFolder,
"base_folder",
utils.GetExecDirectory(),
"base_folder of the app. Use the current path as default value",
)
return baseFolder
}
func (goweb *GoWebApp) ConfigFolder() string {
return filepath.Join(goweb.BaseFolder(), "config")
}
func (goweb *GoWebApp) StorageFolder() string {
return filepath.Join(goweb.BaseFolder(), "storage")
}
func (goweb *GoWebApp) LogFolder() string {
// storage/log
return filepath.Join(goweb.StorageFolder(), "log")
}
func (goweb *GoWebApp) ProvidersFolder() string {
return filepath.Join(goweb.BaseFolder(), "providers")
}
func (goweb *GoWebApp) WebFolder() string {
return filepath.Join(goweb.BaseFolder(), "web")
}
func (goweb *GoWebApp) MiddlewaresFolder() string {
// http/middlewares
return filepath.Join(goweb.WebFolder(), "middlewares")
}
func (goweb *GoWebApp) ConsoleFolder() string {
return filepath.Join(goweb.BaseFolder(), "console")
}
func (goweb *GoWebApp) CommandsFolder() string {
// console/commands
return filepath.Join(goweb.ConsoleFolder(), "commands")
}
func (goweb *GoWebApp) RuntimeFolder() string {
return filepath.Join(goweb.StorageFolder(), "runtime")
}
func (goweb *GoWebApp) TestsFolder() string {
return filepath.Join(goweb.BaseFolder(), "tests")
}

View File

@ -0,0 +1,31 @@
package kernel
import (
"git.vinchent.xyz/vinchent/go-web/framework"
"git.vinchent.xyz/vinchent/go-web/framework/contract"
"github.com/gin-gonic/gin"
)
type GoWebKernelProvider struct {
HttpEngine *gin.Engine
}
func (goweb *GoWebKernelProvider) Register(c framework.Container) framework.NewInstance {
return NewGoWebKernelService
}
func (goweb *GoWebKernelProvider) Init(c framework.Container) error {
return nil
}
func (goweb *GoWebKernelProvider) InstantiateLater() bool {
return false
}
func (goweb *GoWebKernelProvider) Params(c framework.Container) []interface{} {
return []interface{}{goweb.HttpEngine}
}
func (goweb *GoWebKernelProvider) Name() string {
return contract.AppName
}

View File

@ -0,0 +1,22 @@
package kernel
import (
"net/http"
"github.com/gin-gonic/gin"
)
type GoWebKernelService struct {
// Since the container is included in the engine, we don't need to pass
// it as an argument.
engine *gin.Engine
}
func NewGoWebKernelService(params ...interface{}) (interface{}, error) {
httpEngine := params[0].(*gin.Engine)
return &GoWebKernelService{engine: httpEngine}, nil
}
func (goweb *GoWebKernelService) HttpEngine() http.Handler {
return goweb.engine
}

12
framework/utils/exec.go Normal file
View File

@ -0,0 +1,12 @@
package utils
import "os"
// Get the current folder with / suffix
func GetExecDirectory() string {
path, err := os.Getwd()
if err != nil {
return ""
}
return path + "/"
}

53
go.mod Normal file
View File

@ -0,0 +1,53 @@
module git.vinchent.xyz/vinchent/go-web
go 1.22.5
require (
github.com/gin-gonic/gin v1.10.0
github.com/spf13/cobra v1.8.1
)
require (
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.43.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/gin-gonic/gin => ./framework/gin
replace github.com/spf13/cobra => ./framework/cobra

132
go.sum Normal file
View File

@ -0,0 +1,132 @@
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ=
github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

29
handlers.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func UserLoginController(ctx *gin.Context) {
time.Sleep(10 * time.Second)
ctx.JSON(http.StatusOK, "ok")
}
func SubjectDelController(ctx *gin.Context) {
ctx.JSON(http.StatusAccepted, "deleted")
}
func SubjectUpdateController(ctx *gin.Context) {
ctx.JSON(http.StatusAccepted, "updated")
}
func SubjectGetController(ctx *gin.Context) {
ctx.JSON(http.StatusAccepted, "got")
}
func SubjectListController(ctx *gin.Context) {
ctx.JSON(http.StatusAccepted, "list")
}

24
main.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
"git.vinchent.xyz/vinchent/go-web/app/console"
"git.vinchent.xyz/vinchent/go-web/app/web"
"git.vinchent.xyz/vinchent/go-web/framework"
"git.vinchent.xyz/vinchent/go-web/framework/providers/app"
"git.vinchent.xyz/vinchent/go-web/framework/providers/kernel"
)
func main() {
container := framework.NewGoWebContainer()
container.Bind(&app.GoWebAppProvider{})
engine, err := web.NewHttpEngine()
if err != nil {
panic("Cannot start http server")
}
container.Bind(&kernel.GoWebKernelProvider{HttpEngine: engine})
console.RunCommand(container)
}

21
routes.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"git.vinchent.xyz/vinchent/go-web/framework/middlewares"
"github.com/gin-gonic/gin"
)
func registerRouter(core *gin.Engine) {
core.Use(middleware.Test1(), middleware.Test2())
core.GET("/user/login", UserLoginController)
subjectApi := core.Group("/subject")
{
subjectApi.Use(middleware.Test3())
subjectApi.DELETE("/:id", SubjectDelController)
subjectApi.PUT("/:id", SubjectUpdateController)
subjectApi.GET("/:id", SubjectGetController)
subjectApi.GET("/:id/test", SubjectGetController)
subjectApi.GET("/list/all", SubjectListController)
}
}