码迷,mamicode.com
首页 > Web开发 > 详细

创建http客户端,请求其他服务接口(GET/POST)

时间:2017-08-19 15:48:47      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:执行函数   max   message   ==   close   parser   使用   初始化   rom   

service1拥有接口 :

GET      user/{user_id}

POST    user/add

service2调用service1的接口获取数据

1.创建客户端
//定义客户端结构体
type SimpleClient struct {
    Client
    HTTPRequest  *http.Request  //用于返回执行时的request,直接设值无效。引用时注意做非空检查。
    HTTPResponse *http.Response //用于返回执行结果。引用时注意做非空检查。
}
 
//ClientConfig 客户端属性配置
type ClientConfig struct {
RequestID string
EndPointURLToken string //URL Token
OperationName string //链路追踪名或调用函数名
Timeout int //单位为秒
NumMaxRetries int //最大重试次数
RetryMaxTimeDuration time.Duration //总计重试时间 ExpireTime
TraceOption *TraceOption //追踪打印属性
IsExternalURL bool //数据脱敏,默认为FALSE,是否为调用对外api,如是,则不在header的UserAgent中带上内网ip.
IsRESTStatusCode bool //RESTFulAPI风格,使用状态码来表示资源态.
}

//Client 客户端
type Client struct {
ctx context.Context
HTTPClient *http.Client
CustomHeader http.Header
CustomCookie *http.Cookie
EndPointURL string
ClientConfig
}
  //客户端对象构造 func NewSimpleClient(ctx context.Context, url string, configs ...ClientConfig) *SimpleClient { c := &SimpleClient{} c.Client.ctx = ctx c.EndPointURL = url //初始化HTTPClient if len(configs) > 0 { for _, cf := range configs { c.ClientConfig = cf } if c.ClientConfig.Timeout <= 0 { c.ClientConfig.Timeout = 60 } } else {
  //默认配置 c.ClientConfig.NumMaxRetries
= 5 c.ClientConfig.RetryMaxTimeDuration = _MaxRetryTimeDuration c.ClientConfig.Timeout = 60 } if c.ClientConfig.TraceOption == nil { c.ClientConfig.TraceOption = &TraceOption{} } if c.ClientConfig.OperationName != "" { c.OperationName = c.ClientConfig.OperationName } else { c.OperationName = c.EndPointURL } c.HTTPClient = GetHTTPClient(c.ClientConfig.Timeout) //初始化HTTP Header c.CustomHeader = http.Header{} c.initCustomHeader() // c.HTTPRequest = &http.Request{Header: http.Header{}} c.HTTPRequest = &http.Request{Header: c.CustomHeader} return c }

2.GET请求
//Get 发出Get请求
func (sc *SimpleClient) Get() ([]byte, error) {
    defer func() {
        if errPainc := recover(); errPainc != nil {
            stack := strings.Join(strings.Split(string(debug.Stack()), "\n")[2:], "\n")
            logger.Error("[Get] -  stack errPainc:", errPainc)
            err := fmt.Errorf("[Get] recover stack::%s", stack)
            logger.Error("stack:", err)
        }
    }()
    sc.addHeaderRequestID()
    return sc.callGet()
}
//addHeaderRequestID 设置HTTP Header中的RequestID
func (c *Client) addHeaderRequestID() {
    if c.CustomHeader.Get(HeaderXKlookRequestID) != "" {
        return
    }
    if c.ClientConfig.RequestID == "" {
        c.ClientConfig.RequestID = utils.RequestIDFromContext(c.ctx)
    }
    c.CustomHeader.Add(HeaderXKlookRequestID, c.ClientConfig.RequestID)
}
func (sc *SimpleClient) callGet() ([]byte, error) {
    startTime := time.Now()
    sc.HTTPRequest, sc.HTTPResponse = nil, nil
    if sc.EndPointURL == "" {
        return nil, NewRequestErrorV1(sc.RequestID, sc.EndPointURL, "EndPointURL为空.")
    }

    client := sc.GetHTTPClient()
    var err1 error
    req, err := http.NewRequest("GET", sc.EndPointURL, nil)
    if err != nil {
        err1 = fmt.Errorf(" http.NewRequest发生异常. err:%s 耗时:%s ",
            err, time.Since(startTime))
        return nil, NewRequestErrorV1(sc.RequestID, sc.EndPointURL, err1.Error())
    }

    //设置HTTP Header
    copyHTTPHeader(req, sc.CustomHeader)
    if sc.CustomCookie != nil { //设置HTTP.Cookie
        req.AddCookie(sc.CustomCookie)
    }
    //如果是发给第三方的请求
        req.Header.Set(HeaderUserAgent, _DefaultUserAgent)
    
    sc.HTTPRequest = req
    resp, err := client.Do(req)
    if err != nil {
        err1 = fmt.Errorf("client.Do发生异常. err:%s 耗时:%s",
            err, time.Since(startTime))
        return nil, NewRequestErrorV2(sc.RequestID, sc.EndPointURL, err1.Error(), GetHTTPHeaderString(req))
    }
    sc.HTTPResponse = resp
    statusCode := resp.StatusCode
    //需要记录每一次的Header信息
    if sc.ClientConfig.TraceOption.RequestHeader {
        logger.Info(sc.RequestID, " url:", sc.EndPointURL, " ", GetHTTPHeaderString(req),
            " statusCode:", statusCode)
    }

    //RESTFul API常用HTTP状态码来区分状态,然后Body返回状态详情.此时,状态码为非200也是对的.
    if !sc.IsRESTStatusCode {
        if statusCode != http.StatusOK {

            msg := fmt.Sprintf("%s url:%s statusCode:%d %s", sc.RequestID, sc.EndPointURL,
                statusCode, GetHTTPHeaderString(req))
            if resp.Body != nil {
                respBody, err2 := ioutil.ReadAll(resp.Body)
                if err2 != nil {
                    msg += " 且读取RespBody发生异常:" + err2.Error()
                } else {
                    msg += " RespBody:" + string(respBody)
                }
            }
            logger.Info(msg)

            if statusCode >= 400 && statusCode <= 505 {
                err1 = fmt.Errorf("对方服务出现异常状态码. 耗时:%s", time.Since(startTime))
            } else { //如302之类
                err1 = fmt.Errorf("对方服务返回非StatusOK状态码. 耗时:%s", time.Since(startTime))
            }
            resp.Body.Close()

            return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
                statusCode, GetHTTPHeaderString(req))
        }
    }

    if resp.Body == nil {
        resp.Body.Close()
        return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, "resp.Body为空.",
            statusCode, GetHTTPHeaderString(req))
    }

    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        if err == io.EOF {
            err1 = fmt.Errorf("resp.Body已读到EOF. err:%s 耗时:%s", err, time.Since(startTime))
        } else {
            err1 = fmt.Errorf("ioutil.ReadAll读取发生异常. err:%s 耗时:%s", err, time.Since(startTime))
        }
        resp.Body.Close()
        return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
            statusCode, GetHTTPHeaderString(req))
    }
    resp.Body.Close()

    if sc.ClientConfig.TraceOption.RespBody { //记录返回包体
        logger.Info(sc.RequestID, " url:", sc.EndPointURL, " RespBody:", string(respBody))
    }

    //
    //IsRESTFulStatusCode 解析例子
    // retErr, ok := err.(*rest.RequestError)
    // if ok {
    //     t.Log(" \nStatusCode:", retErr.StatusCode(), "\nerr:", retErr.ErrMessage())
    // }
    //
    if sc.IsRESTStatusCode && statusCode != http.StatusOK {
        //当为REST风格,且状态码不为200时,返回Body,及Error,调用方可从Error中取得返回状态码
        return respBody, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, "",
            statusCode, GetHTTPHeaderString(req))
    }

    return respBody, nil
}
3.POST请求
//PostAndParseResult 发出Post请求,并得到标准化的结构体
func (sc *SimpleClient) PostAndParseResult(body []byte) (*KLResultJSON, error) {
    defer func() {
        if errPainc := recover(); errPainc != nil {
            stack := strings.Join(strings.Split(string(debug.Stack()), "\n")[2:], "\n")
            logger.Error("[PostAndParseResult] -  stack errPainc:", errPainc)
            err := fmt.Errorf("[PostAndParseResult] recover stack::%s", stack)
            logger.Error("stack:", err)
        }
    }()

    sc.addHeaderRequestID()
    body, err := sc.callPost(body)
    if err != nil {
        return nil, err
    }
    return ParseResultJSON(body)
}
func (sc *SimpleClient) callPost(body []byte) ([]byte, error) {
    return sc.callHTTPUpdateRequest("POST", body)
}
//callHTTPUpdateRequest post/put执行函数实现
func (sc *SimpleClient) callHTTPUpdateRequest(httpMetchod string, body []byte) ([]byte, error) {
    startTime := time.Now()

    if sc.EndPointURL == "" {
        return nil, NewRequestErrorV1(sc.RequestID, sc.EndPointURL, "EndPointURL为空.")
    }

    if httpMetchod != "DELETE" && body == nil {
        return nil, NewRequestErrorV1(sc.RequestID, sc.EndPointURL, "body为空.")
    }

    sc.HTTPRequest, sc.HTTPResponse = nil, nil
    client := sc.GetHTTPClient()
    if sc.ClientConfig.TraceOption.RequestBody {
        logger.Info(sc.RequestID, " url:", sc.EndPointURL, " RequestBody:", string(body))
    }

    var err, err1 error
    var req *http.Request
    if httpMetchod == "DELETE" {
        req, err = http.NewRequest(httpMetchod, sc.EndPointURL, nil)
    } else {
        req, err = http.NewRequest(httpMetchod, sc.EndPointURL, bytes.NewReader(body))
    }
    if err != nil {
        err1 = fmt.Errorf("http.NewRequest发生异常. err:%s 耗时:%s ",
            err, time.Since(startTime))
        return nil, NewRequestErrorV1(sc.RequestID, sc.EndPointURL, err1.Error())
    }

    //设置HTTP Header
    copyHTTPHeader(req, sc.CustomHeader)
    if sc.CustomCookie != nil { //设置HTTP.Cookie
        req.AddCookie(sc.CustomCookie)
    }
        req.Header.Set(HeaderUserAgent, _DefaultUserAgent)
    
    sc.HTTPRequest = req
    resp, err := client.Do(req)
    if err != nil {
        err1 = fmt.Errorf("client.Do发生异常. err:%s 耗时:%s ",
            err.Error(), time.Since(startTime))
        return nil, NewRequestErrorV2(sc.RequestID, sc.EndPointURL, err1.Error(),
            GetHTTPHeaderString(req))
    }
    defer resp.Body.Close()
    sc.HTTPResponse = resp
    statusCode := resp.StatusCode

    //需要记录每一次的Header信息
    if sc.ClientConfig.TraceOption.RequestHeader {
        logger.Info(sc.RequestID, " url:", sc.EndPointURL, " ", GetHTTPHeaderString(req),
            " statusCode:", statusCode, " status:", resp.Status)
    }

    //RESTFul API常用HTTP状态码来区分状态,然后Body返回状态详情.此时,状态码为非200也是对的.
    if !sc.IsRESTStatusCode {
        if statusCode != http.StatusOK {

            msg := fmt.Sprintf("%s url:%s statusCode:%d %s", sc.RequestID, sc.EndPointURL,
                statusCode, GetHTTPHeaderString(req))
            if resp.Body != nil {
                respBody, err2 := ioutil.ReadAll(resp.Body)
                if err2 != nil {
                    msg += " 且读取RespBody发生异常:" + err2.Error()
                } else {
                    msg += " RespBody:" + string(respBody)
                }
            }
            logger.Info(msg)
            if statusCode >= 400 && statusCode <= 505 {
                err1 = fmt.Errorf("对方服务出现异常. 耗时:%s", time.Since(startTime))
                return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
                    statusCode, GetHTTPHeaderString(req))
            }
            // 在Post情况下,当状态码不为StatusOK时,算执行失败。
            err1 = fmt.Errorf("返回的状态码非StatusOK. 耗时:%s", time.Since(startTime))
            return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
                statusCode, GetHTTPHeaderString(req))
        }
    }

    if resp.Body == nil {
        err1 = fmt.Errorf("resp.Body为空. err:%v  耗时:%s",
            err, time.Since(startTime))
        return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
            statusCode, GetHTTPHeaderString(req))
    }

    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        if err == io.EOF {
            err1 = fmt.Errorf("resp.Body已读到EOF. err:%s 耗时:%s",
                err, time.Since(startTime))
        } else {
            err1 = fmt.Errorf("ioutil.ReadAll读取发生异常. err:%s  耗时:%s",
                err, time.Since(startTime))
        }
        return nil, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, err1.Error(),
            statusCode, GetHTTPHeaderString(req))
    }

    if sc.ClientConfig.TraceOption.RespBody {
        logger.Info(sc.RequestID, " url:", sc.EndPointURL, " RespBody:", string(respBody))
    }

    //
    //IsRESTFulStatusCode 解析例子
    // retErr, ok := err.(*rest.RequestError)
    // if ok {
    //     t.Log(" \nStatusCode:", retErr.StatusCode(), "\nerr:", retErr.ErrMessage())
    // }
    //
    if sc.IsRESTStatusCode && statusCode != http.StatusOK {
        //当为REST风格,且状态码不为200时,返回Body,及Error,调用方可从Error中取得返回状态码
        return respBody, NewRequestErrorV3(sc.RequestID, sc.EndPointURL, "",
            statusCode, GetHTTPHeaderString(req))
    }

    return respBody, nil
}

 

创建http客户端,请求其他服务接口(GET/POST)

标签:执行函数   max   message   ==   close   parser   使用   初始化   rom   

原文地址:http://www.cnblogs.com/fwdqxl/p/7396498.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!