第一章-(服务搭建-路由-上下文)

服务搭建

 

net/http包很轻松得帮我们实现web层服务,该架构基本上都是基于这个包来实现的,由于暂时想不到什么好名字,所以把这个框架命名为finto

接下来就开始吧~~~

 

(一):新建文件夹,命名为kuangjia,新建用于测试的main.go,新建mod文件,并创建框架文件finto,在文件子目录下新建三个文件context.go,finto.go,router.go,文件目录如下所示:

            

 

 

      其中,finto.go文件是框架核心引擎文件,用于创建服务引擎的对象,router.go用于处理路由,包括动态路由,参数匹配等,context.go 为上下文,用于处理请求并返回数据

  main.go用于启动服务,进行测试。

 

(二):编写context.go文件

 

对Web服务来说,无非是根据请求*http.Request,构造响应http.ResponseWriter。但是这两个对象提供的接口粒度太细,比如我们要构造一个完整的响应,需要考虑消息头(Header)和消息体(Body),而 Header 包含了状态码(StatusCode),消息类型(ContentType)等几乎每次请求都需要设置的信息。因此,如果不进行有效的封装,那么框架的用户将需要写大量重复,繁杂的代码,而且容易出错。针对常用场景,能够高效地构造出 HTTP 响应是一个好的框架必须考虑的点。

代码如下:

package finto

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

//起一个别名,使得构建json数据时更加简洁
type D map[string]interface{}

type Context struct {

//请求和响应对象
Writer http.ResponseWriter
Req *http.Request

//请求路径和方法
Path string
Method string

//状态码
StatusCode int
}

func newContext(w http.ResponseWriter,req *http.Request)*Context{
return &Context{
Writer: w,
Req: req,
Path: req.URL.Path,
Method: req.Method,
}
}

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

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

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

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

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

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

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

func (c *Context)Html(code int,html string){
c.SetHeaders("Content-Type", "text/html")
c.SetStatus(code)
c.Writer.Write([]byte(html))
}

通过context.go文件,我们可以自己设置服务请求和返回报文,包括json,data,html格式的数据Context目前只包含了http.ResponseWriter*http.Request,另外提供了对 Method 和 Path 这两个常用属性的直接访问。提供了访问Query和PostForm参数的方法。

 

(三)路由设计,编写router.go文件

代码如下:

package finto

import (
"log"
"net/http"
)

type router struct {
handlers map[string]HandlerFunc
}

func NewRouter() *router {
return &router{handlers:make(map[string]HandlerFunc)}
}


func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
log.Printf("Route %4s - %s", method, pattern)
key := method + "-" + pattern
r.handlers[key] = handler
}

func (r *router) handle(c *Context) {
key := c.Method + "-" + c.Path
if handler, ok := r.handlers[key]; ok {
handler(c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
}
  • 首先定义了类型HandlerFunc,这是提供给框架用户的,用来定义路由映射的处理方法。我们在Engine中,添加了一张路由映射表router,key 由请求方法和静态路由地址构成,例如GET-/GET-/helloPOST-/hello,这样针对相同的路由,如果请求方法不同,可以映射不同的处理方法(Handler),value 是用户映射的处理方法。

  • 当用户调用(*Engine).GET()方法时,会将路由和处理方法注册到映射表 router 中,(*Engine).Run()方法,是 ListenAndServe 的包装。

  • Engine实现的 ServeHTTP 方法的作用就是,解析请求的路径,查找路由映射表,如果查到,就执行注册的处理方法。如果查不到,就返回 404 NOT FOUND 。

(四):核心引擎实现,finto.go文件编写

代码如下:package finto


import (
"net/http"
)

//将路由截取下来,这样就可以添加我们自己的逻辑

type HandlerFunc func(*Context)

type Engine struct{
router *router
}
//核心创建方法
func New()*Engine{
return &Engine{router:NewRouter()}
}


//添加路由
//gin源码的影子

func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
engine.router.addRoute(method, pattern, handler)
}

func (engine *Engine)Get(pattern string,handler HandlerFunc){
engine.addRoute("GET",pattern,handler)
}

func (engine *Engine)Post(pattern string,handler HandlerFunc){
engine.addRoute("POST",pattern,handler)
}



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

//http.ListenAndServe中第二个参数接口中的servehttp方法
//实现ServeHTTP方法
func (engine *Engine)ServeHTTP(w http.ResponseWriter,r *http.Request){
c := newContext(w, r)
engine.router.handle(c)
}

在核心引擎文件中创建了get和post请求的处理方法,并提供run方法用于服务启动,需要注意的是一定要实现serveHTTP方法,
否则在listnandserver时会出错,由于该方法第二个传入参数为interface,定义了serveHTTP方法(我在这里犯错)



至此,整个finto框架的雏形搭建完毕。实现了路由映射表,提供了用户注册静态路由的方法,包装了启动服务的函数,最重要的是我们能够将请求截取下来,
扔到我们自己函数当中,这样我们便可以根据自己的逻辑来进行请求的处理。


(五):服务启动,main.go
代码如下:
package main

import (
"fmt"
"kuangjia/finto"
)


func main(){
fmt.Println("框架启动")
r:= finto.New()
r.Get("/", func(context *finto.Context) {
context.Writer.Write([]byte("hello finto"))
})



r.Run(":9999")
}

:9999表示服务启动在9999端口,运行main文件,在浏览器中输入127.0.0.1:9999按下回车,便能够看到我们自己框架服务的雏形啦!!

 

 



posted @ 2021-11-09 16:41  fanyiaaa  阅读(158)  评论(0编辑  收藏  举报