从路由控制器入手,控制器结构如下,最好看源码和源码上的代码注释,这是最好的材料和答案:
// Controller defines some basic http request handler operations, such as
// http context, template and view, session and xsrf.
type Controller struct {
// context data
Ctx *context.Context
Data map[interface{}]interface{}
// route controller info
controllerName string
actionName string
methodMapping map[string]func() //method:routertree
AppController interface{}
// template data
TplName string
ViewPath string
Layout string
LayoutSections map[string]string // the key is the section name and the value is the template name
TplPrefix string
TplExt string
EnableRender bool
// xsrf data
_xsrfToken string
XSRFExpire int
EnableXSRF bool
// session
CruSession session.Store
}
其中和http相关的是控制器的上下文content。考虑场景:登陆采用的是第三方的系统的Cookie验证,在退出时设置cookie过期时间
// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
// BeegoInput and BeegoOutput provides some api to operate request and response more easily.
type Context struct {
Input *BeegoInput
Output *BeegoOutput
Request *http.Request
ResponseWriter *Response
_xsrfToken string
}
——————————————————————————————————————————————————————————
//登出,设置Cookie过期时间
func (c *ClientLogoutController) Logout() {
//删除相应cookie
c.Ctx.SetCookie("DICT_LOGIN","",1, "/", ".aaa.com")
c.Ctx.SetCookie("DICT_SESS","",1, "/", ".aaa.com")
c.Ctx.SetCookie("DICT_PERS","",1, "/", ".aaa.com")
c.DelSession("iCodeUser")
var m utils.MyException
m.SuccessMessage(nil)
c.Data["json"] = m
c.ServeJSON()
}
因为业务需求需要大量的向第三方服务发起http请求获取信息,所以抽象一个公共方法,可以设置不允许重定向跳转,添加cookie访问,支持多种访问method
package utils
import (
"net/http"
"strings"
"errors"
"io/ioutil"
"fmt"
"github.com/astaxie/beego"
"io"
)
var (
youdao_client *http.Client
stop_redirect *http.Client
)
func StopRedirect(req *http.Request, via []*http.Request) error{
if len(via) >= 0 {
return errors.New("stopped redirects")
}
return nil
}
// 向外发送链接请求
// ctxtype,请求头类型Content-Type, stopRedirect是否允许重定向
func ApiClient(method string, url string,c *beego.Controller, body io.Reader, ctxtype string,
stopRedirect bool) ([]byte, *http.Response , error) {
if youdao_client == nil {
youdao_client = &http.Client{}
}
if stop_redirect == nil {
stop_redirect = &http.Client{CheckRedirect:StopRedirect}
}
req := &http.Request{}
req, _ = http.NewRequest(method, url, body)
// 添加当前cookie
for _, cookie := range c.Ctx.Request.Cookies() {
if strings.HasPrefix(cookie.Name, "DICT") {
req.AddCookie(cookie)
}
}
if ctxtype != ""{
req.Header.Set("Content-Type", ctxtype)
}
//是否进行重定向
var resp *http.Response
var er error
if stopRedirect == true{
resp, er = stop_redirect.Do(req)
} else{
resp, er = youdao_client.Do(req)
}
//排除重定向的错误,重定向的情况下存在空body
if er != nil{
if strings.Contains(er.Error(),"stopped redirects"){
return nil,resp,nil
}
return nil,nil, errors.New(er.Error())
}
defer resp.Body.Close()
//读取body返回信息
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil,nil ,fmt.Errorf("cannot read response data %s", err)
}
return data, resp , nil
}
关于文件的具体操作,采用post form-data表达处理,通过文件操作,转发到第三方系统
//更新用户个人信息,必须采用同步的方式,保证事务一致性
//更新头像上传到oimage,一个图像处理的服务中心,相当于cdn
//更新昵称,同步到第三方系统
func (c *UserController) Update() {
var m utils.MyException
defer func() {
c.Data["json"] = m
c.ServeJSON()
}()
//要保证事物的一致性
o := orm.NewOrm()
o.Begin()
if user, ok := c.GetSession("iCodeUser").(models.User); ok {
// 从form-data中 获取头像,参数1024是文件刷到内存中最大的大小
if err := c.Ctx.Request.ParseMultipartForm(1024); err==nil{
file, _, err := c.Ctx.Request.FormFile("avatar")
if err != nil {
//没有头像参数直接跳到昵称处理
goto NAME
}
//字节流,转发构造请求body
bodyBuf := &bytes.Buffer{}
avatarName := utils.GetRandomStr(13)
if err != nil {
m.FailedMessage("Error writing avatar to buffer")
return
}
defer file.Close()
//处理头像
bodyWriter := multipart.NewWriter(bodyBuf)
// 构造转发请求表单
fileWriter, err := bodyWriter.CreateFormFile("avatar", avatarName)
if err != nil {
fmt.Println("Error writing avatar to buffer")
}
_, err = io.Copy(fileWriter, file)
if err != nil {
fmt.Println("Error writing avatar to io pipeline")
}
//生成form表单
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
oimageLink := "http://oimage.com" //要上传的第三方图片存储服务
_, resp ,err := utils.ApiClient("POST", oimageLink, &c.Controller, bodyBuf, contentType, true)
if err != nil {
m.FailedMessage(err.Error())
return
}
var avatarLink string
//读取oimage返回信息,得到头像地址
if resp.StatusCode == 302{
avatarLink = resp.Header.Get("Location")
if avatarLink ==""{
m.FailedMessage("update avatar error")
return
}
}else{
m.FailedMessage("update avatar error")
return
}
user.Avatar = avatarLink
}
NAME:
if name := c.Input().Get("nickname"); name!=""{
if utf8.RuneCountInString(name)<2 || utf8.RuneCountInString(name) >14{
m.FailedMessage("请输入2~14个字符!")
}
//先向icode数据库中存
_, err := o.Raw("UPDATE user SET name=? where youdao_id=?", name, user.YoudaoID).Exec()
if err != nil {
m.FailedMessage("update to database error")
o.Rollback()
return
}
//向第三方账号系统同步
c.Input().Del("avatar")
c.Input().Add("avatar", user.Avatar)
body := strings.NewReader(c.Input().Encode())
data, _ ,err := utils.ApiClient("POST", "http://ke.youdao.com/user/update.json",&c.Controller ,
body, "application/x-www-form-urlencoded",false)
var ydUpdateInfo youdaoUpdateInfo
err = json.Unmarshal(data, &ydUpdateInfo)
if err != nil {
//回滚操作
o.Rollback()
m.FailedMessage(fmt.Sprintf("response data cannot parse %s", err))
return
}
if ydUpdateInfo.Status != 0{
//回滚操作
o.Rollback()
m.FailedMessage("update info to youdao error")
return
}else{
err = o.Commit()
if err != nil{
o.Rollback()
m.FailedMessage("update error")
return
}
}
user.Name = name
}
data := map[string]interface{}{
"userId": user.YoudaoID,
"avatar": user.Avatar,
"binding": user.Binding,
"phone": user.BindPhone,
"id": user.Id,
"nickName": user.Name,
}
c.SetSession("iCodeUser", user)
m.SuccessMessage(data)
}else{
m.NeedLoginMessage()
}
}
相关文章:https://studygolang.com/articles/2472