开始接触后端开发是从 nodejs 开始,最开始使用的框架是 express,后来陆续接触了其它的框架,觉得最熟悉的还是koa。使用 golang 做后端开发时,对比使用过 gin,echo 以及 iris 三个框架,它们的用法都类似(都支持中间件,中间件的处理也类似),但是在开发过程中还是钟情于 koa 的处理方式,失败则 throw error,成功则将响应数据赋值至 ctx.body,简单易懂。
造一个新的轮子的时候,首先考虑的是满足自己的需求,弱水三千只取一瓢饮,对我来说,新轮子的满足我所需要的一瓢则是重中之重。
elton 参考 koa 的实现,能够简单的添加各类中间件,中间件的执行也和 koa 一样,如下图所示的洋葱图,从外层往内层递进,再从内层返回外层(也可以未至最内层则直接往上返回)。
下面我们先看一下简单的处理成功与出错的例子:
package main import ( "bytes" "errors" "github.com/vicanso/elton" ) func main() { d := elton.New() d.GET("/", func(c *elton.Context) (err error) { c.BodyBuffer = bytes.NewBufferString("hello world") return }) d.GET("/error", func(c *elton.Context) (err error) { err = errors.New("my error") return }) d.ListenAndServe(":7001") }
如代码所示,处理过程非常简单,响应数据由BodyBuffer中获取,如果流程出错,直接返回Error则可。不过例子中的与koa的处理还是有所差距,koa中返回数据时可以随意的指定各种Object(转换为json),而在golang中因为是强类型,所以需要使用interface{}的形式返回,再通过中间件将各类不同的struct转换至BodyBuffer,以及设置Content-Type
。
在 elton 中有两类中间件是必须了解的,一是将 Context.Body(interface{}) 转换至对应的响应数据的 Responder 中间件,另外一个就是将 Error 转换至对应的响应数据的 Error 中间件。在详解这两类中间件之前,我们首先来了解一下 elton 的中间件的一些基本概念。
elton 中间件的处理函数是Handler func(*Context) error
,可以通过Use方法添加至全局的中间件,也可单独添加至单一组或单一的路由处理。中间件处理也非常简单,如果出错,返回 Error(后续的处理函数不再执行)。在当前函数中已完成处理,则无需要调用Context.Next()
,需要转至下一处理函数,则调用Context.Next()
则可。
package main import ( "bytes" "log" "time" "github.com/vicanso/elton" ) func main() { d := elton.New() // logger d.Use(func(c *elton.Context) (err error) { err = c.Next() rt := c.GetHeader("X-Response-Time") log.Printf("%s %s - %s\n", c.Request.Method, c.Request.RequestURI, rt) return }) // x-response-time d.Use(func(c *elton.Context) (err error) { start := time.Now() err = c.Next() c.SetHeader("X-Response-Time", time.Since(start).String()) return }) d.GET("/", func(c *elton.Context) (err error) { c.BodyBuffer = bytes.NewBufferString("hello world") return }) d.ListenAndServe(":7001") }
HTTP 的响应主要分三部分,HTTP 响应状态码,HTTP 响应头以及 HTTP 响应体。前两部分比较简单,格式统一,但是 HTTP 响应体对于不同的应用有所不同。在 elton 的处理中,会将 BodyBuffer 的相应数据在响应时作为 HTTP 响应体输出。在实际应用中,有些会使用 json,有些是 xml 或者自定义的响应格式。因此在elton是提供了Body(interface{}) 属性,允许将响应数据赋值至此字段,再由相应的中间件转换为对应的 BodyBuffer 以及设置Content-Type
。
在实际使用中,HTTP 接口的响应主要还是以json
为主,因此 elton-responder 提供了将 Body 转换为对应的 BodyBuffer(json) 的处理,主要的处理如下:
// New create a responder func New(config Config) elton.Handler { skipper := config.Skipper if skipper == nil { skipper = elton.DefaultSkipper } marshal := standardJSON.Marshal if config.Fastest { marshal = fastJSON.Marshal } return func(c *elton.Context) (err error) { if skipper(c) { return c.Next() } err = c.Next() if err != nil { return } // 如果已设置了BodyBuffer,则已生成好响应数据,跳过 if c.BodyBuffer != nil { return } if c.StatusCode == 0 && c.Body == nil { // 如果status eltone 与 body 都为空,则为非法响应 err = errInvalidResponse return } // 如果body是reader,则跳过 if c.IsReaderBody() { return } ct := elton.HeaderContentType hadContentType := false // 判断是否已设置响应头的Content-Type if c.GetHeader(ct) != "" { hadContentType = true } statusCode := c.StatusCode if statusCode == 0 { statusCode = http.StatusOK } var body []byte if c.Body != nil { switch c.Body.(type) { case string: if !hadContentType { c.SetHeader(ct, elton.MIMETextPlain) } body = []byte(c.Body.(string)) case []byte: if !hadContentType { c.SetHeader(ct, elton.MIMEBinary) } body = c.Body.([]byte) default: // 转换为json buf, err := marshal(c.Body) if err != nil { c.Cod().EmitError(c, err) statusCode = http.StatusInternalServerError he := hes.NewWithErrorStatusCode(err, statusCode) he.Exception = true c.SetHeader(ct, elton.MIMEApplicationJSON) body = he.ToJSON() err = nil } else { if !hadContentType { c.SetHeader(ct, elton.MIMEApplicationJSON) } body = buf } } } c.BodyBuffer = bytes.NewBuffer(body) c.StatusCode = statusCode return nil } }
代码的处理步骤如下:
1、前置判断是否跳过中间件,主要判断条件为:是否出错,或者已设置BodyBuffer
(表示已完成响应数据的处理)或者 Body 为 Reader (以流的形式输出响应数据)。
2、如果 Body 的类型为 string,则将 string 转换为 bytes,如果未设置数据类型,则设置为text/plain; charset=UTF-8
3、如果 Body 的类型为 []byte,如果未设置数据类型,则设置为application/octet-stream
4、对于其它类型,则使用json.Marshal
转换为对应的 []byte,如果未设置数据类型,则设置为application/json; charset=UTF-8
通过此中间件,在开发时可以简单的将各种 struct 对象,map 对象以json
的形式返回,无需要单独处理数据转换,方便快捷。如果应用需要以 xml 等其它形式返回,则可自己编写自定义中间件来做转换处理。
elton 中默认的 Error 处理只是简单的输出err.Error()
,而且状态码也只是简单的使用StatusInternalServerError
,无法满足应用中的各类定制的出错方式。因此一般建议编写自定义的出错处理中间件,根据自定义的 Error 对象生成相应的出错响应数据。如 elton-error-handler 则针对返回的 hes对象对应生成相应的状态码,响应类型以及响应数据(json):
// New create a error handler func New(config Config) elton.Handler { skipper := config.Skipper if skipper == nil { skipper = elton.DefaultSkipper } return func(c *elton.Context) error { if skipper(c) { return c.Next() } err := c.Next() // 如果没有出错,直接返回 if err == nil { return nil } he, ok := err.(*hes.Error) if !ok { he = hes.Wrap(err) he.StatusCode = http.StatusInternalServerError he.Exception = true he.Category = errErrorHandlerCategory } c.StatusCode = he.StatusCode if config.ResponseType == "json" { buf := he.ToJSON() c.BodyBuffer = bytes.NewBuffer(buf) c.SetHeader(elton.HeaderContentType, elton.MIMEApplicationJSON) } else { c.BodyBuffer = bytes.NewBufferString(he.Error()) c.SetHeader(elton.HeaderContentType, elton.MIMETextPlain) } return nil } }
elton 提供更简单方便的 WEB 开发体验,主要实现的代码非常简单,更多的功能都依赖于各类中间件。需要查阅更多的中间件以及文档说明请查阅 github 项目中的说明。
长时间没翻译了,今天捡回来翻译一下Elton John的名曲: Sorry Seems To Be The Hardest 道歉总是最难的事 What have I got to make you love me 到底我要做什么才能让你爱上我 What have I got to make you care 到底我要做什么才能让你在乎我 What do I do when lightning st
music Elton John - Home Again 转载于:https://www.cnblogs.com/runliuv/p/3406460.html
goweb 是 Go 语言实现的一个轻量级的 RESTful 服务框架。
我第一次使用spring batch应用程序,由于框架太灵活了,我有几个关于性能和实现作业的最佳实践的问题,在spring文档中找不到明确的答案。 > 读取由第三方以先前指定的布局发送的具有固定列长值的ASCII文件(第1步读取器) 在oracle数据库上写入有效行(第1步写入器) 执行前一步后,使用第1步的finish时间戳更新数据库中的表(第2步tasklet) 当作业停止时,发送一封电子邮件
Aligorithms and Flow Control 算法和流程控制 Loops 循环 a、避免使用for/in循环 在JavaScript标准中,有四种类型循环。for、for/in、while、do/while,其中唯一一个性能比其他明显慢的是for/in。对于for/in循环的使用场景,更多的是针对不确定内部结构的对象的循环。for/in会枚举对象的命名属性,只有完全遍历对象的所有属性之
DOM Scripting DOM编程 我们都知道对DOM操作的代价昂贵,这往往成为网页应用中的性能瓶颈。在解决这个问题之前,我们需要先知道什么是DOM,为什么他会很慢。 DOM in the Browser World 浏览器中的DOM DOM是一个独立于语言的,使用XML和HTML文档操作的应用程序接口(API)。浏览器中多与HTML文档打交道,DOM APIs也多用于访问文档中的数据。而在浏
Loading and Execution 加载和运行 早前阅读高性能JavaScript一书所做笔记。 从加载和运行角度优化,源于JavaScript运行会阻塞UI更新,JavaScript脚本的下载、解析、运行过程中,页面的下载和解析过程都会停下来等待,因为脚本可能在运行过程中修改页面内容。 Script Positioning 脚本位置 将<script>标签放在尽可能接近<body>标签底
问题内容: 我在将Macruby与ActiveRecord(w /sqlite3)或Sequel一起使用时遇到了问题。还有其他建议吗?我需要一个简单的轻量级持久性机制来嵌入我的应用程序,该机制可以处理少于5个表,最多只能处理几万行。 问题答案: 以下组合有效: 诀窍是卸载“ sqlite3” gem和安装“ sqlite3-ruby”。