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

我正在创建一个 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


弑天下
浏览 228回答 3
3回答

心有法竹

这是因为双case.v仍然是一个interface{}因为它可以是一个*http.Request或一个*http.Responseswitch 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")}这应该工作

小唯快跑啊

除了直接解决您的类型开关问题的其他答案之外,我想指出一个替代解决方案。请注意,该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也可以提供帮助)。

MMMHUHU

你想要做的更像是这样的:switch in.(type) {    case *http.Request:        body = v.(*http.Request).Body    case *http.Response:        body = v.(*http.Response).Body    default:        log.Fatal(...)}编辑:我删除了我的答案的错误部分,请参阅 HectorJ 的答案以了解更多语法,请执行此操作。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go