为了账号安全,请及时绑定邮箱和手机立即绑定

在 Go 中将 interface{} 参数转换为 *http.Request

在 Go 中将 interface{} 参数转换为 *http.Request

Go
弑天下 2021-11-08 10:53:03
我正在创建一个 util 函数,它将读取请求/响应的主体并返回它。这是我目前所做的:func GetBody(in interface{}) []byte {    var body io.Reader    var statusCode int    switch v := in.(type) {        case *http.Request, *http.Response:            body = v.Body            statusCode = v.StatusCode        default:            log.Fatal("Only http.Request and http.Response parameters can be accepted to parse body")    }    if statusCode != 200 {        log.Fatalf("Received status code [%d] instead of [200]", statusCode)    }    body, err := ioutil.ReadAll(body)    if err != nil {        log.Fatal(err)    }    return body}但是我收到一个编译器错误:v.Body undefined (type interface {} is interface with no methods)我是不是遗漏了什么,或者不可能制作一个通用函数来同时为*http.Request和*http.Response
查看完整描述

3 回答

?
心有法竹

TA贡献1866条经验 获得超5个赞

这是因为双case.


v仍然是一个interface{}因为它可以是一个*http.Request或一个*http.Response


switch v := in.(type) {

    case *http.Request

        body = v.Body

        statusCode = v.StatusCode

    case *http.Response:

        body = v.Body

        statusCode = v.StatusCode

    default:

        log.Fatal("Only http.Request and http.Response parameters can be accepted to parse body")

}

这应该工作


查看完整回答
反对 回复 2021-11-08
?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

除了直接解决您的类型开关问题的其他答案之外,我想指出一个替代解决方案。请注意,该interface{}解决方案非常好,并且很容易被认为比这更可取。这是为了启迪。


首先,稍微说一下,如果您感兴趣的是一个通用方法(例如 Write或Cookies)而不是一个通用字段(Body),那么通过自定义界面访问它会更容易也更好。


通过定义一个类型:


type cookier interface { // Should probably use a better name

    Cookies() []*http.Cookie

}  

func ShowCookies1(r cookier) {

    log.Println("Got cookies:", r.Cookies())

}

或者在函数定义中使用匿名类型:


func ShowCookies2(r interface {

    Cookies() []*http.Cookie

}) {

    log.Println("Got cookies:", r.Cookies())

}

这些函数可以接受任何有Cookies方法的东西,包括*http.Request和*http.Response。


不幸的是,在您的特定情况下,您希望访问公共字段而不是公共方法,因此您不能直接使用匹配的接口。


您可以创建一个添加GetBody方法的小型包装器类型(人们可能会争辩说这样的函数应该已在标准包中定义)。


type reqbody struct{ *http.Request }

type respbody struct{ *http.Response }

type getbody interface {

    GetBody() io.ReadCloser

}


func (r reqbody) GetBody() io.ReadCloser  { return r.Body }

func (r respbody) GetBody() io.ReadCloser { return r.Body }


func GetBody2(r getbody) ([]byte, error) {

    body := r.GetBody()

    defer body.Close()

    return ioutil.ReadAll(body)

}

调用者知道他们有什么类型并执行以下操作之一:


    buf, err = GetBody2(reqbody{req})

    buf, err = GetBody2(respbody{resp})

从某种意义上说,这比仅使用interface{}. 但它有一个,而不是有一个函数,它绝对是任何类型和恐慌/错误的好处在运行时,如果一个程序员错误的东西不能适当类型的话来说,这反而迫使调用者安全通过一些你知道的是编译时类型正确。


望着这进一步,你只是读一切从io.ReadCloser再关闭它,所以它可以进一步简化为以下(这可能是比你的interface{}解决方案):


func GetReqBody(r *http.Request) io.ReadCloser   { return r.Body }

// Could add checking r.StatusCode to the following one as well:

func GetRespBody(r *http.Response) io.ReadCloser { return r.Body }

func ReadAndClose(rc io.ReadCloser) ([]byte, error) {

    defer rc.Close()

    return ioutil.ReadAll(rc)

}

同样,调用者知道他们拥有什么类型并执行以下操作之一:


    buf, err = ReadAndClose(GetReqBody(req))

    buf, err = ReadAndClose(GetRespBody(resp))

要不就:


    buf, err = ReadAndClose(req.Body)

    buf, err = ReadAndClose(resp.Body)

您可以在Go Playground上看到所有这些选项的示例。


最后,小心使用ioutil.ReadAll. 通常最好避免将整个文件或网络流预读到缓冲区中,而是在读取时将其作为流处理。特别是,用任意大的 body 发出 HTTP 请求作为拒绝服务攻击或浪费服务器资源是微不足道的(http.MaxBytesReader也可以提供帮助)。


查看完整回答
反对 回复 2021-11-08
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

你想要做的更像是这样的:


switch in.(type) {

    case *http.Request:

        body = v.(*http.Request).Body

    case *http.Response:

        body = v.(*http.Response).Body

    default:

        log.Fatal(...)

}

编辑:我删除了我的答案的错误部分,请参阅 HectorJ 的答案以了解更多语法,请执行此操作。


查看完整回答
反对 回复 2021-11-08
  • 3 回答
  • 0 关注
  • 227 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信