背景
不少项目在出现一些特定的需求时,都会通过请求特定的第三方接口以实现。最近在 001 项目(聚合聊天)中,同样出现了类似的需求(语音转译文字),由博主进行相应的功能开发,由此引入本篇——如何规范的向指定的第三方接口发送请求
因为大部分对外开放接口平台都会具有统一的请求、相应格式,所以需要向指定的第三方接口发送请求、接收回调的时候可以先针对相应的第三方接口平台搭建轮子
规范一:请求发送
根据相应的请求格式搭建请求发送轮子
// 计算签名并发送http请求
func SendRequest(
accessKeyId string,
accessKeySecret string,
timestamp string,
requestUrl string,
urlParams map[string]string,
bodyParams map[string]interface{},
requestMethod string,
contentType string) ([]byte, error) {
if StrIsEmpty(accessKeyId) {
return nil, errors.New("参数access_key_id不能为空")
}
if StrIsEmpty(accessKeySecret) {
return nil, errors.New("参数access_key_secret不能为空")
}
if StrIsEmpty(timestamp) {
return nil, errors.New("参数timestamp不能为空")
}
if StrIsEmpty(requestUrl) {
return nil, errors.New("参数requestUrl不能为空")
}
if urlParams == nil {
return nil, errors.New("参数urlParams不能为null,会带回签名,至少做初始化")
}
if bodyParams == nil {
bodyParams = make(map[string]interface{})
}
if StrIsEmpty(requestMethod) {
return nil, errors.New("参数requestMethod不能为空")
}
if StrIsEmpty(contentType) {
return nil, errors.New("参数contentType不能为空")
}
urlParams["access_key_id"] = accessKeyId
urlParams["timestamp"] = timestamp
signature, signatureNonce := GetSignature(urlParams,
bodyParams,
requestMethod,
contentType,
accessKeySecret)
urlParams["signature"] = signature
urlParams["signature_nonce"] = signatureNonce
urlParams["timestamp"] = timestamp
requestUrl = requestUrl + "?" + UrlFormat(urlParams)
switch requestMethod {
case "POST":
return DoPost(requestUrl, contentType, bodyParams)
case "PATCH":
return DoPatch(requestUrl, contentType, bodyParams)
case "PUT":
return DoPut(requestUrl, contentType, bodyParams)
case "GET":
return DoGet(requestUrl, contentType)
case "DELETE":
return DoDelete(requestUrl, contentType)
default:
return nil, errors.New("支持[GET、POST、PUT、PATCH、DELETE]请求方式")
}
}
这个轮子能够根据传入的参数生成请求URL,计算签名,并发送HTTP请求,支持的请求方法包括GET、POST、PUT、PATCH、DELETE等,函数通过参数校验、签名计算、URL拼接和请求发送等步骤,实现了对HTTP请求的标准化处理
代码通用性强、安全性高、可维护性好,能够很好地满足一般Web应用的请求发送需求,尤其适用于需要签名验证的安全场景
规范二:请求方法
1、初始化固定参数,相应的信息已脱敏
const (
VoiceAsrUrl = ""
VoiceAsrCheckTaskUrl = ""
POST = "POST"
GET = "GET"
)
var (
AccessKeyId string
AccessKeySecret string
ApiHost string
initOnce sync.Once
)
func initConf() {
initOnce.Do(func() {
AccessKeyId = config.GetConf("", "")
AccessKeySecret = config.GetConf("", "")
ApiHost = config.GetConf("", "")
})
}
2、具体的请求发送,包含了语音转译文字和语音转译文字结果两个接口
// 语音转文字结果
func VoiceAsrCheckTask(ctx context.Context, req entity.TranslateVoiceResultReq) (resp entity.TranslateVoiceResultResp, err error) {
initConf()
timestamp := gtools.UnixMsec2Date(gtools.UnixMillis(), types.DateYmdTHis)
reqParams := map[string]string{
"taskId": req.TaskId,
}
url := ApiHost + VoiceAsrCheckTaskUrl
rest, err := util.SendRequest(AccessKeyId, AccessKeySecret, timestamp, url, reqParams, map[string]interface{}{}, GET, "application/json")
if err != nil {
return
}
err = SendResponse(ctx, rest, &resp)
return
}
// 语音转文字
func VoiceAsr(ctx context.Context, req entity.TranslateVoiceReq) (resp entity.TranslateVoiceResp, err error) {
initConf()
timestamp := gtools.UnixMsec2Date(gtools.UnixMillis(), types.DateYmdTHis)
bodyParams := map[string]interface{}{
"voice_url": req.VoiceUrl,
"callback_url": req.CallbackUrl,
"model": "THR-2",
}
url := ApiHost + VoiceAsrUrl
rest, err := util.SendRequest(AccessKeyId, AccessKeySecret, timestamp, url, map[string]string{}, bodyParams, POST, "application/json")
if err != nil {
return
}
err = SendResponse(ctx, rest, &resp)
return
}
规范三:结果响应
func SendResponse(ctx context.Context, req []byte, resp interface{}) (err error) {
var respCommon RespCommon
err = json.Unmarshal(req, &respCommon)
if err != nil {
return
}
if respCommon.Code != ApiOk {
logger.Ex(ctx, "ailab.response", "response =%+v err=%+v", string(req), err)
errMsg := ErrorMessage(respCommon.Code)
err = openerror.New(cerror.ParamError, errMsg)
return
}
_ = json.Unmarshal(respCommon.Data, &resp)
return
}
这个轮子在 RespCommon 中对响应的格式进行封装,同时可以根据解析结果更新响应内容或返回错误,提供了一个通用且有效的响应处理机制,适用于解析和处理各种 API 响应
规范四:打包成工具类
打包成工具类,在 Go 语言中同样是必不可少的一环,合理的打包需要解决循环依赖等问题
写在后面
完成了如上的步骤后,就可以相对规范的进行第三方接口请求啦,最后,希望本文能为读者提供有价值的参考,帮助大家更好地应对类似的技术挑战