[英]Unable to intercept and manipulate HttpServletResponse in Spring Boot
我要求Base64
解碼我的Spring Boot服務接收的每個JSON請求有效負載。 在使用HTTP POST
方法進行發布之前,JSON有效負載將在客戶端進行Base64
編碼。 此外,在呈現給調用客戶端應用程序之前,我還需要Base64
編碼JSON響應。
我需要通過使用處理程序攔截器來減少樣板代碼。 我已經通過使用攔截器實現了操作的請求/傳入分支,但尚未針對響應分支實現。 我已經在下面發布了代碼片段。 攔截響應並對其進行base64編碼的代碼在攔截器類的postHandle方法中。
我在這里做錯了什么?
攔截器類別:
public class Base64ResponseEncodingInterceptor implements HandlerInterceptor {
private static final String DECODED_REQUEST_ATTRIB = "decodedRequest";
private static final String POST = "POST";
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
try {
if (POST.equalsIgnoreCase(request.getMethod())) {
CharResponseWrapper res = new CharResponseWrapper(response);
res.getWriter();
byte[] encoded = Base64.encodeBase64(res.toString().getBytes());
byte[] encoded = Base64.encodeBase64(response.getHeader(ENCODED_RESPONSE_ATTRIB).getBytes());
response.getWriter().write(new String(encoded));
}
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
// preHandle and afterCompletion methods
// Omitted
}
上面使用的CharResponseWrapper
類:
public class CharResponseWrapper extends HttpServletResponseWrapper {
protected CharArrayWriter charWriter;
protected PrintWriter writer;
protected boolean getOutputStreamCalled;
protected boolean getWriterCalled;
public CharResponseWrapper(HttpServletResponse response) {
super(response);
charWriter = new CharArrayWriter();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (getWriterCalled) {
throw new IllegalStateException("getWriter already called");
}
getOutputStreamCalled = true;
return super.getOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
if (getOutputStreamCalled) {
throw new IllegalStateException("getOutputStream already called");
}
getWriterCalled = true;
writer = new PrintWriter(charWriter);
return writer;
}
@Override
public String toString() {
String s = null;
if (writer != null) {
s = charWriter.toString();
}
return s;
}
}
注冊了Interceptor的JavaConfig類:
@Configuration
@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryBean.class, basePackages = "")
@EntityScan(basePackages = { "com.companyname", "com.companyname.productname"})
public class RestConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Base64ResponseEncodingInterceptor());
}
}
使用攔截器的控制器類(此處僅顯示工作請求分支):
@Autowired
HttpServletRequest request;
String decodedRequest = null;
@ModelAttribute("decodedRequest")
public void getDecodedParam(){
decodedRequest = (String) request.getAttribute("decodedRequest");
}
postHandle
方法中的代碼不起作用。 它是HttpServletResponse
為null
還是收到異常消息:
getOutputStream已經被調用
更新:解決方法可直接在ResponseBodyAdvice中讀取響應 。在Controller類中,我添加了以下內容:
@RestController
@RequestMapping("/api/ipmanager")
public class IPProfileRestController extends AbstractRestController {
@Autowired
HttpServletResponse response;
String encodedResponse = null;
@ModelAttribute("encodedResponse")
public void getEncodedResponse(){
response.setHeader("encodedResponse", StringUtils.EMPTY);
}
@RequestMapping(value = "/time", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE }, consumes = {
MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody String saveAccessClientTime(@RequestBody String ecodedRequest) {
// Some code here
String controllerResponse = prettyJson(iPProfileResponse);
response.setHeader("encodedResponse", controllerResponse);
return controllerResponse;
}
}
我在ResponseBodyAdvice中具有以下內容
@ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request,
ServerHttpResponse response) {
String body1 = StringUtils.EMPTY;
// Encode the response and return
List<String> listOfHeaderValues = response.getHeaders().get("encodedResponse");
body1=new String(Base64.encodeBase64(listOfHeaderValues.get(0).getBytes()));
return body1;
}
}
如Spring MVC文檔所述:
HandlerInterceptor
的postHandle
方法並不總是非常適合與@ResponseBody
和ResponseEntity
方法一起使用。 在這種情況下,HttpMessageConverter
在postHandle
之前寫入並提交響應,這使得無法更改響應 ,例如添加標頭。 相反,應用程序可以實現ResponseBodyAdvice
並將其聲明為@ControllerAdvice
bean或直接在RequestMappingHandlerAdapter
上對其進行配置。
話雖這么說:
我在這里做錯了什么?
由於響應已經提交,因此無法更改。 為了更改響應,您應該注冊ResponseBodyAdvice<T>
並將響應編碼邏輯放在此處:
@ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// Encode the response and return
}
}
如果您的方法返回ResponseEntity<T>
則將此代碼寫入HandlerInterceptor
postHandle()
方法中:
例如。 response.addHeader("jwt_token", "kajdlakjd");
它會工作!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.