前言
Golang中,函数是一等公民,使用函数可以实现各种各样的奇淫技巧,比如本文介绍的http全局异常拦截。
在Java中,Spring框架所提供的@ControllerAdvice
注解可以实现对服务器全局异常的统一处理。这种方式大大简化了后端程序的异常处理,比如在处理业务逻辑的过程中,可以将不符合要求的操作或数据直接通过throw
的方式,提前中止后面的不安全操作;而且通过统一异常拦截处理,可以向用户屏蔽一些敏感的异常描述信息,毕竟往往异常描述只是方便开发人员定位问题,对用户来说往往意义不大。
一、闭包是什么?
闭包就是能够读取其他函数内部变量的函数。 例如在Golang中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。 在本质上,闭包是将函数内部和函数外部连接起来的桥梁。例如下面的代码实现斐波那契数列:
package main
import "fmt"
// 使用闭包实现斐波那契数列
func fib() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return b
}
}
func main() {
f := fib()
for i := 0; i < 10; i++ {
fmt.Printf("%d:%d\n", i, f())
}
fmt.Println()
}
上面的代码中,fib()
函数返回体是一个匿名函数,该匿名函数引用了 fib()
函数内部的局部变量 a
和 b
,从而导致 fib
执行结束后,局部变量 a
和 b
并没有随着 fib()
结束而被销毁,使得外部可以通过匿名函数间接操作了 a
和 b
的值。
二、闭包实现全局拦截器
1.定义http处理器
代码如下(示例):
// 定义处理器函数
type appHandler func(writer http.ResponseWriter, request *http.Request) error
2.定义全局异常接口
代码如下(示例):
// 定义自定义业务异常,内嵌包含了 error 接口
type BusinessErr interface {
error
Message() string
Code() int
}
3.实现全局拦截器
代码如下(示例):
// 使用闭包统一处理异常
func errHander(hadler appHandler) func(http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
// 1. 处理全局未捕获异常 panic
defer func() {
if r := recover(); r != nil {
log.Println("Panic: ", r)
http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()
// 2. 处理http请求
err := hadler(writer, request)
// 3. 处理http请求明确的异常信息
if err != nil {
log.Println("Error occur on handling request: ", err.Error())
// 自定义业务异常处理
if userDefineErr, ok := err.(BusinessErr); ok {
http.Error(writer, userDefineErr.Message(), userDefineErr.Code())
return
}
// 系统明确的异常处理
var statusCode int
switch {
case os.IsNotExist(err):
statusCode = http.StatusNotFound
default:
statusCode = http.StatusInternalServerError
}
http.Error(writer, http.StatusText(statusCode), statusCode)
return
}
}
}
4.main
代码如下(示例):
package main
import (
"net/http"
"hello/hello"
)
func main() {
// 定义接口,及其对应的处理器
http.HandleFunc("/hello", errHander(hello.Handler))
// 启动服务器,并监听8080端口
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}
总结
以上就是今天分享的内容,本文仅仅简单介绍了使用闭包实现http全局异常拦截,而Golang函数还有更多高级玩法,值得我们慢慢探索。