Grails 中如何显示网络请求和响应的内容?

grails 开发时,为了调试方便,需要能像其他web框架、chrome 一样,显示出接收到的网络请求以及返回的响应内容,包括header和body内容。

如何实现呢?有现成的插件吗?
SO 有人这样问过了,回答是:

I suggest the following approach (haven’t found anything available for spring-boot when I needed a similar functionality, so I’ve decided to roll my own):

Implement a HttpServletRequestWrapper which will copy the bytes as the InputStream is being consumed so you can re-read the request body multiple times

Implement and register a Filter which will wrap the http request on the way in and log the complete request/response after the request is consumed and the response is produced by the endpoint

Nice bonus is that you can log both request and response in one logging statement, keeping them together in the logfile.

也许我们需要开发一个插件来完成这个需求。

如果使用了 spring-security-core 插件,还可以这样配置 debug filter 来记录安全相关请求日志:

application.yml

# security related config
grails:
    plugin:
        springsecurity:
            debug:
                useFilter: true

会输出下面的日志:

2020-11-04 14:01:27.097  INFO --- [nio-8089-exec-1] g.p.s.web.filter.DebugFilter             : 
************************************************************
Request received for '/':

SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@7cffdda4]

servletPath:/
pathInfo:null

Security filter chain: [
  SecurityRequestHolderFilter
  SecurityContextPersistenceFilter
  MutableLogoutFilter
  GrailsUsernamePasswordAuthenticationFilter
  RestAuthenticationFilter
  SecurityContextHolderAwareRequestFilter
  GrailsRememberMeAuthenticationFilter
  GrailsAnonymousAuthenticationFilter
  GrailsHttpPutFormContentFilter
  UpdateRequestContextHolderExceptionTranslationFilter
  FilterSecurityInterceptor
]
************************************************************

也没有过多有用信息,只有一个 path 和 security filter chain 有点作用。但这个 DebugFilter 类可以作为我们自己实现“请求响应记录插件”的参考。

和 SO 的建议是一样的。


意外发现原来对密码的加密算法封装是在 SpringSecurityCoreGrailsPlugin 类中。

Map<String, PasswordEncoder> idToPasswordEncoder(ConfigObject conf){
...
}

最后结论是,需要自己开发一个插件,来实现记录请求、响应数据的功能。这个插件我已经开发完成,发布到开源社区 https://github.com/yangbo/grails-plugin-log-request,注意,请使用最新版本,新版本修正了一些bug。

实现问题

如何给 FilterBean 传递 grails config 中的开关信息呢?

答:
可以利用 spring 构造函数注入依赖的方法,如下:

// plugin 注册类
securityDebugFilter(classFor('securityDebugFilter', DebugFilter), ref('springSecurityFilterChain'))

// 构造函数
DebugFilter(FilterChainProxy fcp) {
		filterChainProxy = fcp
}

还可以利用 Holders.getConfig() 函数获取配置对象。

def theConfig = Holders.config
logRequestBody = theConfig.get('grails.plugin.logrequest.body.request')
logResponseBody = theConfig.get('grails.plugin.logrequest.body.response')

REST login 接口无返回body的问题

grails.plugin.springsecurity.rest.RestAuthenticationSuccessHandler#onAuthenticationSuccess
	response << renderer.generateJson(authentication as AccessToken)

class HttpServletResponseExtension {
    static leftShift(HttpServletResponse response, arg) {
        response.writer << arg
    }
}

原来是 OutputStreamWriter 没有调用 flush() 方法,没有将 StreamEncoder 缓存的字节写入 outStream 造成的。
OutputStreamWriter 的 close() 方法也不会调用 flush() 方法将 stream encoder 的内容写入 out stream 中,需要显式地调用 OutputStreamWriter 的 flush 方法才行。

于是想到重载 write() 方法,将“写字符串、字符数组”的方法重载,在这些方法后自动调用 flush() 方法。

其实更好的正解应该是让 grails 插件的 filter 来调用 flush() 方法。

于是我们应该重载 logrequest.BufferedHttpServletResponseWrapper#flushBuffer() 方法,在这个方法中调用 PrintWriter 的 flush() 方法。然后再手工调用一下 filterResponse.flushBuffer(),虽然web容器会自动调用 flushBuffer() 方法的,但因为我们需要在容器调用前就使用底层的 output stream 内容,所以需要手工调用一次。

代码如下:

    @Override
    void flushBuffer() throws IOException {
        super.flushBuffer()
        // Solve OutputStreamWriter do not call flush() to dump StreamEncoder data to out-stream issue.
        if (writer) {
            writer.flush()
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值