【问题标题】:How to access attribute of interface如何访问接口的属性
【发布时间】:2015-05-20 17:32:45
【问题描述】:

我打算在两个响应结构的标头和正文中都使用 HTTP 状态代码。但是,没有将状态码设置为函数参数两次,并再次为结构体设置以避免冗余。

JSON() 的参数response 是一个允许两个结构都被接受的接口。编译器抛出以下异常:

response.Status undefined (type interface {} has no field or method Status)

因为响应字段不能有状态属性。有没有另一种方法可以避免两次设置状态码?

type Response struct {
    Status int         `json:"status"`
    Data   interface{} `json:"data"`
}

type ErrorResponse struct {
    Status int      `json:"status"`
    Errors []string `json:"errors"`
}

func JSON(rw http.ResponseWriter, response interface{}) {
    payload, _ := json.MarshalIndent(response, "", "    ")
    rw.WriteHeader(response.Status)
    ...
}

【问题讨论】:

    标签: interface go


    【解决方案1】:

    rw.WriteHeader(response.Status) 中的类型responseinterface{}。在 Go 中,您需要显式声明底层结构的类型,然后访问该字段:

    func JSON(rw http.ResponseWriter, response interface{}) {
        payload, _ := json.MarshalIndent(response, "", "    ")
        switch r := response.(type) {
        case ErrorResponse:
            rw.WriteHeader(r.Status)
        case Response:
            rw.WriteHeader(r.Status) 
        }
        ...
    }
    

    然而,更好的首选方法是为您的响应定义一个通用接口,该接口具有获取响应状态的方法:

    type Statuser interface {
        Status() int
    }
    
    // You need to rename the fields to avoid name collision.
    func (r Response) Status() int { return r.ResStatus }
    func (r ErrorResponse) Status() int { return r.ResStatus }
    
    func JSON(rw http.ResponseWriter, response Statuser) {
        payload, _ := json.MarshalIndent(response, "", "    ")
        rw.WriteHeader(response.Status())
        ...
    }
    

    最好将 Response 重命名为 DataResponseResponseInterface 重命名为 Response,IMO。

    【讨论】:

    • 感谢您的解决方案。现在最好将状态代码设置为新参数两次并在结构内部还是编写两个新函数和一个接口来实现相同的效果?
    • nit:DataResponse 可能比OKResponse 更好。 Status() int 接口的另一个名称是 Statuser 或只是 Status (前者听起来不对,但这种…er 非单词接口有优先级)。
    • 感谢@Dave-C。稍微改了一下答案。
    • @user3147268 IMO 最好使用该界面。您还可以创建一个实现Status() 的基本通用结构,但这只是过头了。在 Go 中重复一点总是可以的。
    • 命名是您的偏好。去任何你喜欢的东西。附带说明一下,在 Go 中,如果一个接口包装了一个方法 M,他们称该接口为 'M'er。查看io.Readerio.Writerio.ReadWriteCloser 以获得一个想法。
    【解决方案2】:

    接口没有属性,所以需要从接口中提取结构体。为此,您使用type assertion

    if response, ok := response.(ErrorResponse); ok {
        rw.WriteHeader(response.Status)
        ...
    

    【讨论】:

    • 你为什么只使用response.(ErrorResponse)
    • @user3147268:我不明白这个问题
    • 使用response.(ErrorResponse)response.(Response)有什么区别吗?
    • 不同之处在于你得到一个ErrorResponse 或者你得到一个Response。如果您希望它们可互换,请创建一个获得所需属性的新接口。
    • 那将是Soheil Hassas Yeganeh的方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多