Java/Spring
Springboot OncePerRequestFilter를 이용한 HttpServeletRequest의 body 값 무제한으로 읽어보기, request body 한번만 읽어올수 있는 문제 해결
닥치고개발
2023. 7. 10. 18:05
728x90
우선 다양한 방법으로 request body 한번만 읽어올수 있는 문제를 해결 할 수 있지만 내가 해결 한방 법중에 가장 심플한 방법을 소개한다.
- 필터를 만든다.
- 끝!!!
너무 간단해서 할말을 잃었다.
- 그럼 코드를 보면서 한번 이해해 보자
커스텀 필터를 아래와 같이 만든다!!!
Order는 1번으로 최상단!!
/** * JSON Body를 여러번 읽을 수 있게 커스텀 필터를 추가 */ @Component @Order(1) public class RequestBodyFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { chain.doFilter(new MultiReadHttpServletRequest(request), response); } private class MultiReadHttpServletRequest extends HttpServletRequestWrapper { private ByteArrayOutputStream cachedBytes; public MultiReadHttpServletRequest(HttpServletRequest request) { super(request); } @Override public ServletInputStream getInputStream() throws IOException { if (cachedBytes == null) { cacheInputStream(); } return new CachedServletInputStream(cachedBytes.toByteArray()); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } private void cacheInputStream() throws IOException { /* Cache the inputstream in order to read it multiple times. For * convenience, I use apache.commons IOUtils */ cachedBytes = new ByteArrayOutputStream(); IOUtils.copy(super.getInputStream(), cachedBytes); }
/* An input stream which reads the cached request body */
private static class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream buffer;
public CachedServletInputStream(byte[] contents) {
this.buffer = new ByteArrayInputStream(contents);
}
@Override
public int read() {
return buffer.read();
}
@Override
public boolean isFinished() {
return buffer.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
throw new RuntimeException("Not implemented");
}
}
}
}
## 이후 나는 이 값을 interceptor에서 처리하기 위해서 Requestbody값을 읽을 필요가 있었다.
- 그래서 인터셉터를 아래와 같이 만들어서 사용중이다
- 인증 관련 처리를 하고 있다.
```java
@Slf4j
public class AuthorizationInterceptor implements HandlerInterceptor {
public static final String X_PARTNER_NAME = "X-Partner-Name";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String requestJsonBody = getRequestJsonBody(request);
log.info("requestJsonBody : " + requestJsonBody);
log.info("start check");
checkIfClientValid(request);
log.info("end check");
// BniTokenService.instance.get()
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* 유효한 Client인지 체크
*
* @param request
*/
private void checkIfClientValid(HttpServletRequest request) {
Client.findByClient(request.getHeader(X_PARTNER_NAME));
}
/**
* JSON Body를 읽어온다
*
* @param request
* @return
*/
private String getRequestJsonBody(HttpServletRequest request) {
try {
// JSON 인 경우만 Body를 읽도록 필터링
if ("application/json".equalsIgnoreCase(request.getHeader("Content-Type"))) {
return IOUtils.readLines(request.getInputStream()).stream().map(f -> f.trim()).collect(Collectors.joining());
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
return null;
}
}
여기서 유효한 Client가 아니면 throw를 던진다.
- 끝!!! 쉽지 않은가?
728x90