RestTemplate request forwarding exception ERR_CONTENT_DECODING_FAILED 200 (OK)

#1 Problem Description

In the based Spring Bootproject, the function of request forwarding (using the exchange method of RestTemplate) was implemented. Suddenly, net::ERR_CONTENT_DECODING_FAILED 200 (OK)an error was reported on the front end, and both the backend and upstream system logs showed that the request had been completed.

#2 Reason Exploration

The literal meaning of the above error 内容解码失败is that the browser cannot decode normally after getting the back-end data. At this point, we look at the encoding of the request response.

We can see that the upstream system is enabled 响应压缩, and then the reading method of the transit system is:

restTemplate.exchange(entity, String::class.java)

Therefore, when compression is enabled in the response of the upstream system, the transfer system reads Stringand returns it to the front end. After getting the data, the browser recognizes that it is gzipencoded through the response header and tries to decompress it, resulting in the exception that occurred previously.

#3 Fix

It is actually very simple to repair. 字节数组Just read the response in a format in the transfer system (compatible with responses in various formats of the upstream system). The complete code is as follows:

class ServiceRoute {
    
    
    val logger = LoggerFactory.getLogger(javaClass)

    val restTemplate = RestTemplate().also {
    
      }

    fun redirect(request:HttpServletRequest, response:HttpServletResponse, targetUrl:String, extraHeaders: Map<String, String?>?=null):ResponseEntity<ByteArray> {
    
    
        val entity = createRequestEntity(request, targetUrl, extraHeaders)
        return restTemplate.exchange(entity, ByteArray::class.java)
    }

    @Throws(URISyntaxException::class, IOException::class)
    private fun createRequestEntity(request: HttpServletRequest, url: String, extraHeaders: Map<String, String?>?): RequestEntity<*> {
    
    
        val httpMethod = HttpMethod.valueOf(request.method)
        val headers = parseRequestHeader(request)
        extraHeaders?.forEach {
    
     (k, v) -> headers.add(k, v) }

        //将原始请求转换为字节数组
        val body = StreamUtils.copyToByteArray(request.inputStream)
        return RequestEntity<Any>(body, headers, httpMethod, URI(url))
    }

    /**
     * 复制原始请求的 header 信息
     */
    private fun parseRequestHeader(request: HttpServletRequest): MultiValueMap<String, String?> {
    
    
        val headers = HttpHeaders()
        val headerNames: List<String> = Collections.list(request.headerNames)
        for (headerName in headerNames) {
    
    
            val headerValues: List<String> = Collections.list(request.getHeaders(headerName))
            for (headerValue in headerValues) {
    
    
                headers.add(headerName, headerValue)
            }
        }
        return headers
    }
}

Usage example

@RequestMapping("route/**", name = "转发请求")
fun redirect(response:HttpServletResponse):ResponseEntity<*> {
    val path = request.servletPath.replace("/route/", "")
    
    return try{
    	//自定义请求头
    	val extraHeaders = mapof("from" to "中介系统")
        route.redirect( request, response, "http://localhost:8080/${path}", extraHeaders ).also {
            //此处可查看返回内容
        }
    }
    catch (e:Exception) {
        logger.error("[SERVICE-ROUTE] 转发失败", e)
        ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
    }
    finally {
       	//此处可以做一些后续操作
    }
}

Guess you like

Origin blog.csdn.net/ssrc0604hx/article/details/132206350