728x90
HttpServletRequestWrapper 커스텀
interceptor, filter 등에서 getInputStream() 을 통해 한 번 request 바디 내용을 읽어들이면
이후에 다른 filter 나 controller 에서 해당 내용을 읽을 수 없음 (일회용)
⇒ 해당 내용을 저장해서 가지고 있다가 여러 번 반복해서 부를 수 있도록
HttpServletRequestWrapper 를 상속받은 MyRequestWrapper 정의
MyRequestWrapper
- HttpServletRequestWrapper 상속
- 생성자에서 request 의 Inputstream 을 읽어와서 전역 변수에 저장
- 읽어들인 메시지 내용을 반복해서 제공할 수 있도록 `getInputStream()` 과 `getReader()` 오버라이딩 → 전역변수에 저장된 내용으로 InputStream 을 매번 다시 만들어서 제공
import org.springframework.util.StreamUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class MyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public MyRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream()); // 실제 요청 request body 내용 읽어서 저장
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
return new ServletInputStream() {
@Override
public boolean isFinished() { return false; }
@Override
public boolean isReady() { return false; }
@Override
public void setReadListener(ReadListener readListener) { }
@Override
public int read() throws IOException {
return byteArrayInputStream.read(); // 매번 같은 stream 을 제공
}
};
}
@Override
public BufferedReader getReader() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
return new BufferedReader(new InputStreamReader(byteArrayInputStream, "UTF-8")); // 매번 같은 stream 제공
}
@Override
public ServletRequest getRequest() { return super.getRequest(); }
}
Filter
- Filter 인터페이스를 구현한 InputstreamFilter 작성
- doFilter 의 filterChain.doFilter() 메서드에 매개변수로 위에서 만든 래퍼 클래스 제공
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@RequiredArgsConstructor
@Component
public class InputstreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
MyRequestWrapper myRequestWrapper = new MyRequestWrapper(httpRequest);
filterChain.doFilter(myRequestWrapper, servletResponse); // 내가 만든 래퍼 클래스 제공
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
FilterConfig
- 필터를 등록
import com.example.ajax.filter.InputstreamFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<InputstreamFilter> inputstreamFilterFilterRegistrationBean() {
FilterRegistrationBean<InputstreamFilter> registry = new FilterRegistrationBean<>();
registry.setFilter(new InputstreamFilter());
registry.addUrlPatterns("/qna/update", "/qna/delete", "/qna/mark"); // 해당 필터를 적용하고자 하는 곳
return registry;
}
}
사용방법
- 기존 방식 : request 의 body 내용을 한 번 읽어들였기 때문에 컨트롤러에서 해당 요청을 처리하는 핸들러에서 request 의 body 내용을 읽어들이려 하면 에러가 발생
inputStream = hi.getInputStream(); // request 로부터 inputstream 얻어오기
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 메시지 바디를 읽어들여서 문자열로 리턴
// DefaultHandlerExceptionResolver :
// Resolved [org.springframework.http.converter.HttpMessageNotReadableException:
// Required request body is missing
- 변경한 방식 : request 의 body 내용을 저장해두었다가 inputstream 을 요청할 때마다 같이 제공하기 때문에 여러 번 재사용할 수 있음
MyRequestWrapper wrapper = new MyRequestWrapper(request);
String messageBody = wrapper.getReader().readLine();
728x90
'Java | Spring' 카테고리의 다른 글
| [Java] 제네릭의 공변성 (0) | 2025.03.13 |
|---|---|
| [Spring] 스프링 Validation 의 Errors 와 BindingResult (0) | 2025.03.10 |
| [Spring] 예외 처리 - ExceptionResolver (0) | 2025.03.09 |
| [Spring] @ControllerAdvice, @RestControllerAdvice (0) | 2025.03.09 |
| [Spring] @RequestParam, @RequestBody, @ModelAttribute (1) | 2025.03.09 |