Go Http相关操作

  • pqdong 

从路由控制器入手,控制器结构如下,最好看源码和源码上的代码注释,这是最好的材料和答案:

// 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

发表评论

电子邮件地址不会被公开。 必填项已用*标注