基本思路要求
2019/3/22 22:54晚间终于走通了大致的拦截流程。
项目需求,由于项目国际化需要,本公司所有系统翻译配置信息使用集中集群方式配置在apollo配置中心里面,最好的方式当然是吧所有的状态码都统一在路由层处理,但是同事实测nginx+lua解析服务返回json严重影响路由效率,因此java项目和go项目都统一使用自身拦截器处理,降低业务层逻辑的耦合。
java spirng boot项目的拦截器很丰富,也很容易实现,问题是go使用的这个框架iris查看源码所有的返回客户端的消息直接io.write写回去,并且其本身并没有提供responseHand的相关接口。因此处理参考文章末尾写了一半的源码解析,只能自己再别写边改。
思路如下:
返回客户端的数据主要有write,wrietString,JSON,JSONP,这几种类型,当然还有文件留之类的的,这里我们并不考虑此内容,本文也不做拦截。
熟悉源码之后可以发现,整个底层都是使用go自身的http.server和io实现的,只是在上面包装了一层,我们这里也只着重关注包装的router和context这两个类和方法,它们才是http交互的关键。因为http.Hander接口它赋值的是app.Router所以Route是进行请求和回写客户端的关键。所有的htttp请求都会使用router.mainHandler进行处理,该方法会通过HandleRequest(ctx)找到系统启动时注册的url和对应的处理方法名,使用该方法处理。
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
ctx := cPool.Acquire(w, r)
router.requestHandler.HandleRequest(ctx) //主要http请求入口
cPool.Release(ctx)
}
参数ctx则附带了大量http请求的url,参数等上下文信息,同时也是通过url找到对应处理方法后的主要入口ctx.Do(n.Handlers)
n := t.search(path, ctx.Params())
if n != nil {
ctx.SetCurrentRouteName(n.RouteName)
ctx.Do(n.Handlers) //调用对应的方法进行处理
// found
return
}
看到这里你应该明白了,所有的Controler中都会带上它的值contex,当然也包括上面说的给客户的回写消息,主要靠的也是它的实现接口,通过每个回写的方法拦截器中回调我们自己实现的具体拦截业务即可起到拦截内容后返回给它们继续后续处理的问题。而我们自己实现的具体拦截器当然最好是统一一个地方处理,在contex中各个write,JSON等方法统一处理即可,这样改写代码很少便可以实现拦截。
而go语言可以使用反射实现方法回调,因此实现起来还是很简单的。
http.server
Handler:{ServeHTTP(ResponseWriter, *Request)}这里既是实现了该接口的Application.Router
Application
*router.APIBuilder
*router.Router 实现了http.Handler接口
ContextPool *context.Pool 里面存放系统变量
新建 (route,Usercontroller)
ControllerActivator
router: url methond(fullpack,name)
value:
未完待续,后面我会加上更多内容:
本文作者:bamboo
email:zjcjava@163.com
具体修改方法
修改route.go方法
type Router struct {
MyHand interface{} // 新增自定义字段
}
// the important
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
ctx := cPool.Acquire(w, r)
ctx.ResponseHandler(router.MyHand)//添加我的拦截器
router.requestHandler.HandleRequest(ctx)
cPool.Release(ctx)
}
修改context.go方法
type Context interface {
ResponseHandler(o interface{})//给context赋值方法
}
type context struct {
//新增自定义拦截器字段
HandResponseWriter interface{}
}
// 实现该接口
func (ctx *context) ResponseHandler(o interface{}) {
ctx.HandResponseWriter = o
}
// 自定义拦截器调用该自定义字段,通过方法名反射找到方法
// ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.
func (ctx *context) HandResponse(str string) string {
fmt.Println("----------HandResponse拦截START-----------------------------")
if ctx.HandResponseWriter!=nil {
//ctx.HandResponseWriter.HandResponseWriter(str)
o := ctx.HandResponseWriter
t := reflect.ValueOf(o) //反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
mv := t.MethodByName("HandResponseWriter")
args := []reflect.Value{reflect.ValueOf(str)}
rs :=mv.Call(args)
str =rs[0].Interface().(string)
}
fmt.Println(str)
fmt.Println("----------HandResponse拦截END-----------------------------")
return str
}
//使用IO之前先调用拦截方法
func (ctx *context) Write(rawBody []byte) (int, error) {
rawBody = []byte(ctx.HandResponse(string(rawBody)))
return ctx.writer.Write(rawBody)
}
// Writef formats according to a format specifier and writes to the response.
//
// Returns the number of bytes written and any write error encountered.
func (ctx *context) Writef(format string, a ...interface{}) (n int, err error) {
return ctx.writer.Writef(format, a...)
}
// WriteString writes a simple string to the response.
//
// Returns the number of bytes written and any write error encountered.
func (ctx *context) WriteString(body string) (n int, err error) {
body = ctx.HandResponse(body)
return ctx.writer.WriteString(body)
}
// JSON marshals the given interface object and writes the JSON response to the client.
func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
options := DefaultJSONOptions
if len(opts) > 0 {
options = opts[0]
}
ctx.ContentType(ContentJSONHeaderValue)
if options.StreamingJSON {
if ctx.shouldOptimize() {
var jsoniterConfig = jsoniter.Config{
EscapeHTML: !options.UnescapeHTML,
IndentionStep: 4,
}.Froze()
enc := jsoniterConfig.NewEncoder(ctx.writer)
err = enc.Encode(v)
} else {
enc := json.NewEncoder(ctx.writer)
enc.SetEscapeHTML(!options.UnescapeHTML)
enc.SetIndent(options.Prefix, options.Indent)
err = enc.Encode(v)
}
if err != nil {
ctx.StatusCode(http.StatusInternalServerError) // it handles the fallback to normal mode here which also removes the gzip headers.
return 0, err
}
return ctx.writer.Written(), err
}
jsonStr, err := json.Marshal(v)
if err != nil {
fmt.Println("json err: ", err)
}
//转成json字符串 调用拦截方法
str :=ctx.HandResponse(string(jsonStr));
json.Unmarshal([]byte(str), v) // 调用完成解析成原对象中
n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize())
if err != nil {
ctx.StatusCode(http.StatusInternalServerError)
return 0, err
}
return n, err
}
使用
package routers
import "fmt"
type MyHand struct {
}
func (ctx MyHand) HandResponseWriter(str string) string {
fmt.Println("----------MyHand-----------------------------")
fmt.Println(str)
// 检查字符串是否符合基本的json格式
if( strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")){
// 字符串转JSON
var obj interface{}
err := json.Unmarshal([]byte(str), &obj)
if err != nil {
fmt.Println(err)
}
objMap := obj.(map[string]interface{}) //转成Map
code:=objMap["code"]
fmt.Println("状态码是:",code)
json, err := json.Marshal(objMap)// 转回JSON
if err != nil {
fmt.Println("json err: ", err)
}
//str=`{"code":"0000","msg":"失败","data":{"stat":"bamboo"}}`
str=string(json)
}
fmt.Println(str)
fmt.Println("----------MyHand-----------------------------")
return str
}
给app.Router.MyHand赋值
myHand := MyHand{}
app.Router.MyHand=myHand
参考资料
Iris框架源码阅读和分析
https://www.cnblogs.com/rabbix/p/10332845.html