首页 > 解决方案 > 恐慌,因为参数必须是结构而不是接口,在 web.Router?

问题描述

在测试以下代码时:

package logHandler

import (
    "ezsoft/apiserver_sdk/context"

    "github.com/hsoshiant/web"
)

// Build simply builds a log handler router
//
// Parameters:
//
// - `mainRouter` : the main Router
//
// Returns:
//
// - a sub Router made specifically for logging
func Build(mainRouter *web.Router) *web.Router {
    return mainRouter.Subrouter(context.Context{}, "/log").
        Post("/", doLog)

}

我惊慌失措。context.Context是这样定义的:

//BaseContext contains all the base context to be helpful when using the utilities
type BaseContext struct {
    Session        session.Store
    ResponseWriter *web.ResponseWriter
    RequestReader  *web.Request
}

//Context contains all values needed when using the utilities
type Context struct {
    BaseContext
    JSONBody      map[string]interface{}
    tokenHandler  *apiToken.APITokenHandlerSt
    OAuthInstance *oAuth2.OAuth2St
}

考虑到可测试性/灵活性,开发人员编写了中间件函数 take context.ContextIntf,它context.Context实现了,定义如下:

//ContextIntf is the interface to use when using the context interface functions.
type ContextIntf interface {
    SetUniversalHeaders(header map[string]string) map[string]string
    GetInfo() *Context
    GetOAuth() *oAuth2.OAuth2St
}

这是一个这样的中间件函数:

//AuthenticationMiddleware Middleware which handles all of the authentication.
func AuthenticationMiddleware(mw AuthenticationMiddlewareIntf, context context.ContextIntf, w web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
    //Check if the url should be public.
    for _, url := range mw.GetInfo().nonAuthURLs {
        if url.Method == r.Method && strings.Contains(r.URL.Path, url.DomainName) {
            key := utilities.GetRemoteAdd(r) + ";" + r.URL.Path
            if timeSince, ok := NonAuthSecurityMap[key]; ok {
                var (
                    timeSinceTime, _  = time.Parse(time.UnixDate, timeSince)
                    timeSinceDuration = time.Since(timeSinceTime)
                )
                if timeSinceDuration < nonAuthTimeReset {
                    // will sleep for `nonAuthTimeReset` - `timeSinceDuration` > 0
                    time.Sleep(-(timeSinceDuration - nonAuthTimeReset))
                }
            }
            NonAuthSecurityMap[key] = time.Now().Format(time.UnixDate)
            next(w, r)
            return
        }
    }

    if errSt := CheckForAuthorization(mw, context, r, w); errSt != nil {
        responses.Write(w, responses.Unauthorized(*errSt))
        return
    }
    defer context.GetInfo().Session.SessionRelease(w)
    next(w, r)
}

我不确定package web正在检查哪些中间件业务功能,更不用说它们驻留在哪些业务包中,并且调用堆栈跟踪没有返回此类线索。

因此,我得到的错误是:


                            * You are adding a handler to a router with context type 'Context'
                            *
                            *
                            * Your handler function can have one of these signatures:
                            *
                            * // If you don't need context:
                            * func YourFunctionName(rw web.ResponseWriter, req *web.Request)
                            *
                            * // If you want your handler to accept a context:
                            * func (c *Context) YourFunctionName(rw web.ResponseWriter, req *web.Request)  // or,
                            * func YourFunctionName(c *Context, rw web.ResponseWriter, req *web.Request)
                            *
                            * Unfortunately, your function has this signature: func(context.ContextIntf, web.ResponseWriter, *web.Request)
                            *
                            ************************************************************************************************************************

为什么这请求一个Context结构,而不是ContextIntf它实现的那个?!

堆栈跟踪

看起来像这样:

goroutine 20 [running]:
testing.tRunner.func1(0xc04213e1e0)
    C:/Go/src/testing/testing.go:742 +0x2a4
panic(0x7633c0, 0xc0421320a0)
    C:/Go/src/runtime/panic.go:502 +0x237
github.com/hsoshiant/web.validateHandler(0x778420, 0x80d8f0, 0x13, 0x8405c0, 0x7b7960)
    D:/dev2017/GO/src/github.com/hsoshiant/web/router_setup.go:286 +0x242
github.com/hsoshiant/web.(*Router).addRoute(0xc042106680, 0x7eeb93, 0x4, 0x7ee4bd, 0x1, 0x778420, 0x80d8f0, 0xc042051f80)
    D:/dev2017/GO/src/github.com/hsoshiant/web/router_setup.go:223 +0x94
github.com/hsoshiant/web.(*Router).Post(0xc042106680, 0x7ee4bd, 0x1, 0x778420, 0x80d8f0, 0xc042106680)
    D:/dev2017/GO/src/github.com/hsoshiant/web/router_setup.go:193 +0x6f
ezsoft/apiserver_sdk/logger/logHandler.Build(0xc0421064e0, 0xc042051f40)
    D:/dev2017/GO/src/ezsoft/apiserver_sdk/logger/logHandler/handler.go:20 +0xcf
ezsoft/apiserver_sdk/logger/logHandler.TestBuild.func1(0xc04213e1e0)
    D:/dev2017/GO/src/ezsoft/apiserver_sdk/logger/logHandler/handler_test.go:16 +0x91
testing.tRunner(0xc04213e1e0, 0x80d8e0)
    C:/Go/src/testing/testing.go:777 +0xd7
created by testing.(*T).Run
    C:/Go/src/testing/testing.go:824 +0x2e7

更新:它击中了私有方法doLog,它是这样定义的:

func doLog(contextIntf context.ContextIntf, rw web.ResponseWriter, req *web.Request) {
    var (
        logType int    = 0
        code    string = ""
        message string = ""
        context        = contextIntf.GetInfo()
    )
    if val, OK := context.JSONBody["type"]; OK {
        if val1, noErr := val.(float64); noErr {
            logType = int(val1)
        }
    }
    if logType == 0 {
        responses.Write(rw, responses.FreeUnprocessableEntity("Type"))
        return
    }

    if val, OK := context.JSONBody["code"]; OK {
        if val1, noErr := val.(string); noErr {
            code = val1
        } else {
            responses.Write(rw, responses.FreeUnprocessableEntity("Code"))
            return
        }
    }

    if val, OK := context.JSONBody["message"]; OK {
        if val1, noErr := val.(string); noErr {
            message = val1
        }
    }
    if message == "" {
        responses.Write(rw, responses.FreeUnprocessableEntity("message"))
        return
    }
    if code > "" {
        code = " (" + code + ") "
    }
    switch logType {
    case 1:
        logger.Instance.LogError(code + message)
    case 2:
        logger.Instance.LogWarning(code + message)
    case 3:
        logger.Instance.LogInfo(code + message)
    default:
        logger.Instance.LogWarning(code + message)
    }

    responses.Write(rw, responses.OK(0))
}

我仍然不明白为什么这个论点需要是 a context.Context,或者我作为单元测试人员能做些什么。

标签: unit-testinggo

解决方案


推荐阅读