BackEnd/Spring

[Spring MVC] Filter에서 파라미터 처리하는 방법

Potwings 2024. 8. 1. 21:26

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

 

GitHub - Potwings/filter_want_request: 필터는 request가 보고 싶다

필터는 request가 보고 싶다. Contribute to Potwings/filter_want_request development by creating an account on GitHub.

github.com