Beego源码解析之Context-Output

Beego源码解析之Context-Output


BeegoOutput

  • 初始化结构体方法
  • 写入response body的方法:写入数据类型可以为[]byte,JSON,JSONP,XML,YAML。
  • 一个根据header中Accept字段,自动判断用哪种数据类型写入response body。
  • 一系列参数判断和设置参数值的方法(具体情况,具体分析,这里不做呈现)。
  • 一个下载文件的response

BeegoOutput 结构

BeegoOutput主要用来发送回传的header

type BeegoOutput struct {
	Context    *Context
	Status     int
	EnableGzip bool
}

初始化Onput

在/beego/context.go中初始化context时被调用,beego的output封装request的一些api,便于使用。Reset在context.Reset()中被调用。reset的作用相当于初始化BeegoOutput。

func NewOutput() *BeegoOutput {
	return &BeegoOutput{}
}
// Reset init BeegoOutput
func (output *BeegoOutput) Reset(ctx *Context) {
	output.Context = ctx
	output.Status = 0
}

根据[]byte设置repsonse的body

EnableGzip为true表示,需根据request的Accept-Encoding值来对content进行压缩。

如果encoding的值不为空,会赋值给b,并且赋值output.Header的Content-Encoding。

如果encoding的值不为空,Content-Length为压缩后的content长度,否则为原始content长度。

可以实现io.Copy的原因见注释

outupt.status是啥和在哪里赋值不清楚

func (output *BeegoOutput) Body(content []byte) error {
	var encoding string
	var buf = &bytes.Buffer{}
	if output.EnableGzip {
		encoding = ParseEncoding(output.Context.Request)
	}
	if b, n, _ := WriteBody(encoding, buf, content); b {
		output.Header("Content-Encoding", n)
		output.Header("Content-Length", strconv.Itoa(buf.Len()))
	} else {
		output.Header("Content-Length", strconv.Itoa(len(content)))
	}
	// Write status code if it has been set manually
	// Set it to 0 afterwards to prevent "multiple response.WriteHeader calls"
	if output.Status != 0 {
		output.Context.ResponseWriter.WriteHeader(output.Status)
		output.Status = 0
	} else {
		output.Context.ResponseWriter.Started = true
	}
    
    //output.Context.ResponseWriter继承/net/http/server.go里的ResponseWriter,/net/http/server.go里的Response被同文件里的response结构体实现,在response里有一个类型为*bytes.Writer的字段w,w里面实现继承了io.Writer,所以可以进行如下io.Copy。
	io.Copy(output.Context.ResponseWriter, buf)
	return nil
}

根据json设置response的body

JSON封装了上面讲到的Body方法,主要是将传进来的json序列化之后写入body。

hasIndent表示需要做缩排。

encoding表示需要将utf-8转成unicode。

stringsToJSON和原来思考的copy方式不太一样,值得研究,说不定后面有用。

func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) error {
   output.Header("Content-Type", "application/json; charset=utf-8")
   var content []byte
   var err error
   if hasIndent {
      content, err = json.MarshalIndent(data, "", "  ")
   } else {
      content, err = json.Marshal(data)
   }
   if err != nil {
      http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
      return err
   }
   if encoding {
      content = []byte(stringsToJSON(string(content)))
   }
   return output.Body(content)
}

根据YAML data写入repsonse的body
func (output *BeegoOutput) YAML(data interface{}) error {
   output.Header("Content-Type", "application/x-yaml; charset=utf-8")
   var content []byte
   var err error
   content, err = yaml.Marshal(data)
   if err != nil {
      http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
      return err
   }
   return output.Body(content)
}

根据JSONP data写入response的body

Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

为什么我们从不同的域(网站)访问数据需要一个特殊的技术(JSONP )呢?这是因为同源策略。

同源策略,它是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。

func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error {
   output.Header("Content-Type", "application/javascript; charset=utf-8")
   var content []byte
   var err error
   if hasIndent {
      content, err = json.MarshalIndent(data, "", "  ")
   } else {
      content, err = json.Marshal(data)
   }
   if err != nil {
      http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
      return err
   }
   callback := output.Context.Input.Query("callback")
   if callback == "" {
      return errors.New(`"callback" parameter required`)
   }
   callback = template.JSEscapeString(callback)
   callbackContent := bytes.NewBufferString(" if(window." + callback + ")" + callback)
   callbackContent.WriteString("(")
   callbackContent.Write(content)
   callbackContent.WriteString(");\r\n")
   return output.Body(callbackContent.Bytes())
}

根据XML data写入response的body
func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error {
   output.Header("Content-Type", "application/xml; charset=utf-8")
   var content []byte
   var err error
   if hasIndent {
      content, err = xml.MarshalIndent(data, "", "  ")
   } else {
      content, err = xml.Marshal(data)
   }
   if err != nil {
      http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
      return err
   }
   return output.Body(content)
}

根据header中的Accept字段决定需要用哪个类型写入response的body

// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) {
   accept := output.Context.Input.Header("Accept")
   switch accept {
   case ApplicationYAML:
      output.YAML(data)
   case ApplicationXML, TextXML:
      output.XML(data, hasIndent)
   default:
      output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0])
   }
}

定义一个response为download file
func (output *BeegoOutput) Download(file string, filename ...string) {
   // check get file error, file not found or other error.
   if _, err := os.Stat(file); err != nil {
      http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file)
      return
   }

   var fName string
   if len(filename) > 0 && filename[0] != "" {
      fName = filename[0]
   } else {
      fName = filepath.Base(file)
   }
   output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fName))
   output.Header("Content-Description", "File Transfer")
   output.Header("Content-Type", "application/octet-stream")
   output.Header("Content-Transfer-Encoding", "binary")
   output.Header("Expires", "0")
   output.Header("Cache-Control", "must-revalidate")
   output.Header("Pragma", "public")
   http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file)
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值