Servlet의 Filter를 구현하는 Filter클래스에서 POST로 전달되는 Body에서 JSON을 불러와 처리해야할 일이 있었다.
따라서 Filter에서 아래와 같이 Request에서 값을 불러왔다.
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletInputStream inputStream = request.getInputStream();
ObjectMapper objectMapper = new ObjectMapper();
PersonDTO dto = objectMapper.readValue(inputStream, PersonDTO.class);
System.out.println("name: " + dto.getName());
chain.doFilter(request, response);
}
허나 이 코드에는 문제가 있는데...
Request의 Body는 InputStream으로 이루어져있어 한 번 데이터를 읽으면 다시 사용할 수 없다.
이로 인해 Body의 값을 이미 Filter에서 한 번 사용하면 Controller에서는 정상적으로 값을 불러올 수 없었다.
해결 방법
이에 대한 해결법으로는 Body의 값을 읽어와 저장해둔 후 여러번 사용하는 방법이 있었다.
(참고 : https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times)
HttpServletRequestWrapper을 상속받은 클래스를 구현하여 이 작업을 진행한다.
기존 Request의 Body는 한번밖에 못 불러오므로
해당 값을 불러와 RequestWrapper의 클래스 변수(requestData)로 저장해둔 후 여러번 사용한다.
public class RequestWrapper extends HttpServletRequestWrapper {
//기존 Request의 Body 불러와 저장해두는 변수
private String requestData;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestData = requestDataByte(request);
}
private String requestDataByte(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = request.getInputStream();
byte[] rawData = StreamUtils.copyToByteArray(inputStream); //Body 복제 진행
return new String(rawData);
}
@Override
public ServletInputStream getInputStream() {
// requestData에 있는 값 Stream으로 변환하여 return
ByteArrayInputStream inputStream = new ByteArrayInputStream(
this.requestData.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return inputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
throw new UnsupportedOperationException();
}
@Override
public int read() {
return inputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
또 Filter 작업 완료 후 이후에 Request를 사용하는 작업에서 RequestWrapper를 사용할 수 있도록
chain.doFilter() 시 기존 Request가 아닌 RequestWrapper를 전달해준다.
public class CommonFilter implements Filter {
....
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 필터 로직
HttpServletRequest httpRequest = (HttpServletRequest) request;
RequestWrapper requestWrapper = new RequestWrapper(httpRequest);
String method = httpRequest.getMethod();
try {
if (method.equals("POST")) {
ServletInputStream inputStream = requestWrapper.getInputStream();
ObjectMapper objectMapper = new ObjectMapper();
PersonDTO dto = objectMapper.readValue(inputStream, PersonDTO.class);
System.out.println("name: " + dto.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
chain.doFilter(requestWrapper, response); //requestWrapper를 전달
}
...
}
이렇게하면 Filter와 Controller 모두에서 Request의 Body값을 불러와 사용할 수 있었다.
작업 코드
https://github.com/Potwings/filter_want_request
'BackEnd > Spring' 카테고리의 다른 글
어노테이션은 사실 깡통이다. (0) | 2024.09.22 |
---|---|
Spring Boot main 2배 버그 팝니다@@@@@ (0) | 2024.09.18 |
gradle 프로젝트가 build 안될 때 - java.lang.IllegalStateException: Module entity with name (3) | 2023.05.16 |
Spring AntPathMatcher URL 체크 시 OR 조건 매핑 (0) | 2023.03.02 |