上一篇文章中我们对Server进行了抽象,但是在具体接口的实现过程中,还是存在一些问题,比如我们实现一个注册接口
重复的代码我们要写上很多遍,这个时候我们就可以对Context也就是上下文进行抽象
首先定义一个结构体,用于json的序列化和反序列化
type signUpReq struct{
Email string `json:"email"`
PassWord string `json:"password"`
ConfirmedPassword string `json:"confirmed_pwd"`
}
定义一个Context结构体
type Context struct{
W http.ResponseWriter
R *http.Request
}
声明一个方法接收器,用于处理json字符串
func (c *Context) ReadJson(data interface{}) error {
body,err := io.ReadAll(c.R.Body)
if err!= nil{
return err
}
return json.Unmarshal(body,data)
}
实现注册接口
func SignUp(w http.ResponseWriter,r *http.Request){
c := web.Context(w,r)
req := signUpReq{}
err := c.ReadJson(req)
if err != nil {
fmt.Fpringf(w,"请求出错:%v\n",err)
}
//没有异常返回一个虚拟用户id
fmt.Fprintf(w, "%d", 1)
}
此时虽然代码简洁了一些,但仍然存在问题,就是与fmt库存在强耦合,并且很难给客户端返回json格式数据,也没有响应码
写入响应
//commomResponse 定义响应结构
type commonResponse struct {
BizCode int `json:"biz_code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
func SignUp(w http.ResponseWriter,r *http.Request){
c := web.Context(w,r)
req := signUpReq{}
err := c.ReadJson(req)
if err != nil {
resp := &commonResponse{
BizCode:500,
Msg: fmt.Springf("请求出错:%v",err)
}
respBytes,_ := jsonMarshal(resp)
fmt.Fpringf(w,string(respBytes))
return
}
}
//正确的返回逻辑,还得再写一遍
fmt.Fprintf(w, "%d", 1)
定义处理响应的方法接收器
func (c *Context) WriteJson(code int, resp interface{}) error {
c.W.WriteHeader(code)
respJson, err := json.Marshal(resp)
if err != nil {
return err
}
_, err = c.W.Write(respJson)
return nil
}
辅助方法
然后我们可以提供一些辅助方法,但是要注意的是,实际场景中要考虑是否需要提供某些方法
func (c *Context) OkJson(resp interface{}) error {
return c.WriteJson(http.StatusOK, resp)
}
func (c *Context) NotFoundJson(resp interface{}) error {
return c.WriteJson(http.StatusNotFound, resp)
}
func (c *Context) ServerErrorJson(resp interface{}) error {
return c.WriteJson(http.StatusInternalServerError, resp)
}
func (c *Context) BadRequestJson(resp interface{}) error {
return c.WriteJson(http.StatusBadRequest, resp)
}
此时我们的注册接口,已经可以进化成下面的样子了
func SignUpWithContext(w http.ResponseWriter,r *http.Request) {
c := web.Context(w,r)
req := &signUpReq{}
err := ctx.ReadJson(req)
if err != nil {
c.BadRequestJson(&commonResponse{
BizCode:500,
Msg: fmt.Springf("请求出错:%v",err)
})
return
}
resp := &commonResponse{
BizCode: 200,
Msg: "ok",
Data:1
}
err = c.OkJson(resp)
if err != nil {
fmt.Printf("请求出错:%v",err)
}
}
我们已经对Context以及接口的实现进行了一定程度上的改造,但是仍然存在问题,就是Context是由用户进行创建的,也就是用户有着一定的自由度,而我们希望由框架来创建Context
func SignUpWithContext(ctx *Context) {
req := &signUpReq{}
err := ctx.ReadJson(req)
if err != nil {
c.BadRequestJson(&commonResponse{
BizCode:500,
Msg: fmt.Springf("请求出错:%v",err)
})
return
}
resp := &commonResponse{
BizCode: 200,
Msg: "ok",
Data:1
}
err = ctx.WriteJson(http.StatusOK, resp)
if err != nil {
fmt.Printf("Response err:%v", err)
}
}
此时,原有的Route方法已经无法使用了,需要进行改造
改造Route方法
type Server interface{
Route(pattern string,handlerFunc func(c *Context))
Start(address string) error
}
func (s *sdkHttpServer) Route(pattern string,handlerFunc func(c *Context)){
http.HandleFunc(pattern,func(weiter http.ResponseWriter, request *http.Request)){
c := NewContext(writer, request)
handlerFunc(c)
}
}
到这里我们已经完成了以下内容
- 可以从http.Request中读取数据,
- 往http.ResponseWriter 中写入响应和数据
- json数据的序列化和反序列化
- 自定义Context上下文
下一步我们希望这个小框架能支持RESTFul Api