[Spring] Interceptor 와 request.getInputStream()

2025. 3. 10. 09:54·Java | Spring
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
'Java | Spring' 카테고리의 다른 글
  • [Java] 제네릭의 공변성
  • [Spring] 스프링 Validation 의 Errors 와 BindingResult
  • [Spring] 예외 처리 - ExceptionResolver
  • [Spring] @ControllerAdvice, @RestControllerAdvice
cyyy
cyyy
    250x250
  • cyyy
    Dev Log
    cyyy
  • 전체
    오늘
    어제
    • 분류 전체보기 (71)
      • Web | Network (14)
      • JavaScript (17)
      • Node.js (13)
      • Java | Spring (17)
        • GitHub Clone Project (6)
      • DB (4)
      • Linux (4)
      • AWS (2)
      • IDE (0)
  • 최근 글

  • 인기 글

  • 태그

    JavaScript
    github clone
    프로토타입
    db
    네트워크
    promise
    Database
    Network
    Java
    프로토콜
    node.js
    자바스크립트
    Linux
    spring boot
    노드
    스프링
    prototype
    bcrypt
    비동기
    Spring
  • 링크

    • Notion Log
  • hELLO· Designed By정상우.v4.10.3
cyyy
[Spring] Interceptor 와 request.getInputStream()
상단으로

티스토리툴바