Compare commits

..

4 Commits

Author SHA1 Message Date
286f010346 feat: if report http server close err as run err 2024-01-13 18:55:13 +08:00
f938487884 fix: serve listener 2024-01-13 17:45:44 +08:00
7c03a40ef0 feat: add simple readme 2024-01-13 15:05:46 +08:00
f0fc5fa44f alpha: v0.0.2 2024-01-12 21:42:29 +08:00
8 changed files with 251 additions and 59 deletions

47
app.go
View File

@ -1,8 +1,11 @@
package nf package nf
import ( import (
"context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strings" "strings"
) )
@ -12,6 +15,7 @@ type App struct {
config *Config config *Config
router *router router *router
groups []*RouterGroup groups []*RouterGroup
server *http.Server
} }
func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) { func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
@ -36,9 +40,46 @@ func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
} }
} }
func (a *App) Run(address string) error { func (a *App) run(ln net.Listener) error {
srv := &http.Server{Handler: a}
a.server = srv
if !a.config.DisableBanner { if !a.config.DisableBanner {
fmt.Println(banner + "nf serve at: " + address + "\n") fmt.Println(banner + "nf serve at: " + ln.Addr().String() + "\n")
} }
return http.ListenAndServe(address, a)
err := a.server.Serve(ln)
if !errors.Is(err, http.ErrServerClosed) || a.config.ErrServeClose {
return err
}
return nil
}
func (a *App) Run(address string) error {
ln, err := net.Listen("tcp", address)
if err != nil {
return err
}
return a.run(ln)
}
func (a *App) RunTLS(address string, tlsConfig *tls.Config) error {
ln, err := tls.Listen("tcp", address, tlsConfig)
if err != nil {
return err
}
return a.run(ln)
}
func (a *App) RunListener(ln net.Listener) error {
a.server = &http.Server{Addr: ln.Addr().String()}
return a.run(ln)
}
func (a *App) Shutdown(ctx context.Context) error {
return a.server.Shutdown(ctx)
} }

72
ctx.go
View File

@ -3,8 +3,11 @@ package nf
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log" "log"
"mime/multipart"
"net"
"net/http" "net/http"
"strings" "strings"
) )
@ -91,6 +94,11 @@ func (c *Ctx) Form(key string) string {
return c.Request.FormValue(key) return c.Request.FormValue(key)
} }
func (c *Ctx) FormFile(key string) (*multipart.FileHeader, error) {
_, fh, err := c.Request.FormFile(key)
return fh, err
}
func (c *Ctx) Query(key string) string { func (c *Ctx) Query(key string) string {
return c.Request.URL.Query().Get(key) return c.Request.URL.Query().Get(key)
} }
@ -104,6 +112,14 @@ func (c *Ctx) Get(key string, defaultValue ...string) string {
return value return value
} }
func (c *Ctx) IP() string {
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
if err != nil {
return ""
}
return ip
}
func (c *Ctx) BodyParser(out interface{}) error { func (c *Ctx) BodyParser(out interface{}) error {
var ( var (
err error err error
@ -152,5 +168,61 @@ func (c *Ctx) BodyParser(out interface{}) error {
} }
func (c *Ctx) QueryParser(out interface{}) error { func (c *Ctx) QueryParser(out interface{}) error {
//v := reflect.ValueOf(out)
//
//if v.Kind() == reflect.Ptr && v.Elem().Kind() != reflect.Map {
//}
return parseToStruct("query", out, c.Request.URL.Query()) return parseToStruct("query", out, c.Request.URL.Query())
} }
/* ===============================================================
|| Handle Ctx Response Part
=============================================================== */
func (c *Ctx) Status(code int) *Ctx {
c.StatusCode = code
c.Writer.WriteHeader(code)
return c
}
func (c *Ctx) Set(key string, value string) {
c.Writer.Header().Set(key, value)
}
func (c *Ctx) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}
func (c *Ctx) SendString(data string) error {
c.SetHeader("Content-Type", "text/plain")
_, err := c.Write([]byte(data))
return err
}
func (c *Ctx) Writef(format string, values ...interface{}) (int, error) {
c.SetHeader("Content-Type", "text/plain")
return c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
func (c *Ctx) JSON(data interface{}) error {
c.SetHeader("Content-Type", "application/json")
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(data); err != nil {
return err
}
return nil
}
func (c *Ctx) Write(data []byte) (int, error) {
return c.Writer.Write(data)
}
func (c *Ctx) HTML(html string) error {
c.SetHeader("Content-Type", "text/html")
_, err := c.Writer.Write([]byte(html))
return err
}

4
nf.go
View File

@ -9,6 +9,10 @@ type Map map[string]interface{}
type Config struct { type Config struct {
// Default: 4 * 1024 * 1024 // Default: 4 * 1024 * 1024
BodyLimit int64 `json:"-"` BodyLimit int64 `json:"-"`
// if report http.ErrServerClosed as run err
ErrServeClose bool `json:"-"`
DisableBanner bool `json:"-"` DisableBanner bool `json:"-"`
DisableLogger bool `json:"-"` DisableLogger bool `json:"-"`
DisableRecover bool `json:"-"` DisableRecover bool `json:"-"`

67
readme.md Normal file
View File

@ -0,0 +1,67 @@
# NF Web Framework
### Usage
##### basic usage
- get param
```go
func main() {
app := nf.New()
app.Get("/hello/:name", func(c *nf.Ctx) error {
name := c.Param("name")
return c.JSON(nf.Map{"status": 200, "data": "hello, " + name})
})
log.Fatal(app.Run("0.0.0.0:80"))
}
```
- parse request query
```go
func handleQuery(c *nf.Ctx) error {
type Req struct {
Name string `query:"name"`
Addr []string `query:"addr"`
}
var (
err error
req = Req{}
)
if err = c.QueryParser(&req); err != nil {
return nf.NewNFError(400, err.Error())
}
return c.JSON(nf.Map{"query": req})
}
```
- parse application/json body
```go
func handlePost(c *nf.Ctx) error {
type Req struct {
Name string `json:"name"`
Addr []string `json:"addr"`
}
var (
err error
req = Req{}
reqMap = make(map[string]any)
)
if err = c.BodyParser(&req); err != nil {
return nf.NewNFError(400, err.Error())
}
// can parse body multi times
if err = c.BodyParser(&reqMap); err != nil {
return nf.NewNFError(400, err.Error())
}
return c.JSON(nf.Map{"struct": req, "map": reqMap})
}
```

48
resp.go
View File

@ -1,49 +1 @@
package nf package nf
import (
"encoding/json"
"fmt"
)
func (c *Ctx) Status(code int) *Ctx {
c.StatusCode = code
c.Writer.WriteHeader(code)
return c
}
func (c *Ctx) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}
func (c *Ctx) SendString(data string) error {
c.SetHeader("Content-Type", "text/plain")
_, err := c.Write([]byte(data))
return err
}
func (c *Ctx) Writef(format string, values ...interface{}) (int, error) {
c.SetHeader("Content-Type", "text/plain")
return c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
func (c *Ctx) JSON(data interface{}) error {
c.SetHeader("Content-Type", "application/json")
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(data); err != nil {
return err
}
return nil
}
func (c *Ctx) Write(data []byte) (int, error) {
return c.Writer.Write(data)
}
func (c *Ctx) HTML(html string) error {
c.SetHeader("Content-Type", "text/html")
_, err := c.Writer.Write([]byte(html))
return err
}

View File

@ -3,6 +3,7 @@ package main
import ( import (
"github.com/loveuer/nf" "github.com/loveuer/nf"
"log" "log"
"net"
) )
func main() { func main() {
@ -13,5 +14,6 @@ func main() {
return c.JSON(nf.Map{"status": 200, "data": "hello, " + name}) return c.JSON(nf.Map{"status": 200, "data": "hello, " + name})
}) })
log.Fatal(app.Run("0.0.0.0:80")) ln, _ := net.Listen("tcp", ":80")
log.Fatal(app.RunListener(ln))
} }

View File

@ -18,13 +18,18 @@ func main() {
var ( var (
err error err error
req = new(Req) req = new(Req)
rm = make(map[string]any)
) )
if err = c.QueryParser(req); err != nil { //if err = c.QueryParser(req); err != nil {
return nf.NewNFError(400, err.Error()) // return nf.NewNFError(400, "1:"+err.Error())
//}
if err = c.QueryParser(&rm); err != nil {
return nf.NewNFError(400, "2:"+err.Error())
} }
return c.JSON(nf.Map{"status": 200, "data": req}) return c.JSON(nf.Map{"status": 200, "data": req, "map": rm})
}) })
log.Fatal(app.Run("0.0.0.0:80")) log.Fatal(app.Run("0.0.0.0:80"))

49
xtest/quit/main.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"context"
"github.com/loveuer/nf"
"log"
"time"
)
func main() {
app := nf.New()
quit := make(chan bool)
app.Get("/name", handleGet)
go func() {
err := app.Run(":7383")
log.Print("run with err=", err)
}()
go func() {
time.Sleep(5 * time.Second)
err := app.Shutdown(context.TODO())
log.Print("quit with err=", err)
quit <- true
}()
<-quit
log.Print("quited")
}
func handleGet(c *nf.Ctx) error {
type Req struct {
Name string `query:"name"`
Addr []string `query:"addr"`
}
var (
err error
req = Req{}
)
if err = c.QueryParser(&req); err != nil {
return nf.NewNFError(400, err.Error())
}
return c.JSON(nf.Map{"req_map": req})
}