框架結構參考另一篇文章golang基於context的web範式。git
簡單http server實現github
簡單http 框架實現golang
直接上框架簡化實現的代碼:web
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
type (
// App 框架主體
App struct {
Addr string
Logger
Router
Server
Middlewares []MiddlewareFunc
}
// HandleFunc 請求處理函數
HandleFunc func(*Context) // MiddlewareFunc 中間件函數 MiddlewareFunc func(HandleFunc) HandleFunc // Logger 日誌輸出接口 Logger interface {
Print(...interface{})
Printf(string, ...interface{})
}
// Router 路由器接口
Router interface {
Match(string, string) HandleFunc
RegisterFunc(string, string, HandleFunc)
}
// Server 服務啓動接口
Server interface {
ListenAndServe() error
}
// Context 請求上下文,封裝請求操做,未詳細實現。
Context struct {
*http.Request
http.ResponseWriter
Logger
}
// MyRouter 基於map和遍歷實現的簡化路由器
MyRouter struct {
RoutesConst map[string]HandleFunc
RoutesPath []string
RoutesFunc []HandleFunc
}
// MyLogger 輸出到標準輸出的日誌接口實現
MyLogger struct {
out io.Writer
}
)
// NewApp 函數建立一個app。
func NewApp() *App {
return &App{
Addr: ":8088",
Logger: &MyLogger{},
Router: &MyRouter{},
}
}
// Run 方法啓動App。
func (app *App) Run() error {
// Server初始化
if app.Server == nil {
app.Server = &http.Server{
Addr: app.Addr,
Handler: app,
}
}
app.Printf("start server: %s", app.Addr)
return app.Server.ListenAndServe()
}
// ServeHTTP 方式實現http.Hander接口,處理Http請求。
func (app *App) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
ctx := &Context{
Request: req,
ResponseWriter: resp,
Logger: app,
}
// 路由匹配
h := app.Router.Match(ctx.Method(), ctx.Path())
// 處理中間件
for _, i := range app.Middlewares {
h = i(h)
}
// 處理請求
h(ctx)
}
// AddMiddleware App增長一個處理中間件。
func (app *App) AddMiddleware(m ...MiddlewareFunc) {
app.Middlewares = append(app.Middlewares, m...)
}
// Print 方法日誌輸出,實現Logger接口。
func (l *MyLogger) Print(args ...interface{}) {
if l.out == nil {
l.out = os.Stdout
}
fmt.Print(time.Now().Format("2006-01-02 15:04:05 - "))
fmt.Fprintln(l.out, args...)
}
// Printf 方法日誌輸出,實現Logger接口。
func (l *MyLogger) Printf(format string, args ...interface{}) {
l.Print(fmt.Sprintf(format, args...))
}
// Match 方法匹配一個Context的請求,實現Router接口。
func (r *MyRouter) Match(method, path string) HandleFunc {
// 查找路由
path = method + path
h, ok := r.RoutesConst[path]
if ok {
return h
}
for i, p := range r.RoutesPath {
if strings.HasPrefix(path, p) {
return r.RoutesFunc[i]
}
}
return Handle404
}
// Handle404 函數定義處理404響應,沒有找到對應的資源。
func Handle404(ctx *Context) {
ctx.ResponseWriter.WriteHeader(404)
ctx.ResponseWriter.Write([]byte("404 Not Found"))
}
// RegisterFunc 方法註冊路由處理函數,實現Router接口。
func (r *MyRouter) RegisterFunc(method string, path string, handle HandleFunc) {
if r.RoutesConst == nil {
r.RoutesConst = make(map[string]HandleFunc)
}
path = method + path
if path[len(path)-1] == '*' {
r.RoutesPath = append(r.RoutesPath, path)
r.RoutesFunc = append(r.RoutesFunc, handle)
} else {
r.RoutesConst[path] = handle
}
}
// Method 方法獲取請求方法。
func (ctx *Context) Method() string {
return ctx.Request.Method
}
// Path 方法獲取請求路徑。
func (ctx *Context) Path() string {
return ctx.Request.URL.Path
}
// RemoteAddr 方法獲取客戶端真實地址。
func (ctx *Context) RemoteAddr() string {
xforward := ctx.Request.Header.Get("X-Forwarded-For")
if "" == xforward {
return strings.SplitN(ctx.Request.RemoteAddr, ":", 2)[0]
}
return strings.SplitN(string(xforward), ",", 2)[0]
}
// WriteString 方法實現請求上下文返回字符串。
func (ctx *Context) WriteString(s string) {
ctx.ResponseWriter.Write([]byte(s))
}
// MiddlewareLoggerFunc 函數實現日誌中間件函數。
func MiddlewareLoggerFunc(h HandleFunc) HandleFunc {
return func(ctx *Context) {
ctx.Printf("%s %s %s", ctx.RemoteAddr(), ctx.Method(), ctx.Path())
h(ctx)
}
}
func main() {
app := NewApp()
app.AddMiddleware(MiddlewareLoggerFunc)
app.RegisterFunc("GET", "/hello", func(ctx *Context) {
ctx.WriteString("hello micro web")
})
app.Run()
}
複製代碼