Spring Filter에서 request Body 가공하기

2021. 7. 19. 17:53·Tech/Spring

 

목적 : 컨텐츠 검색시 싱글쿼터( ' )가 포함된 검색어가 있는경우 \를 붙여주기

마이바티스를 사용할수 밖에 없는 쿼리이며 mysql은 자바처럼 싱글쿼터를 \을 붙여줘야 인식하기 때문에

특정 컨트롤러에서 치환해도 되지만 공부할겸 필터에서 가공하는 코드를 작성해봤다.

 

@ServletComponentScan
@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(VodmanagerApplication.class);
    }

}
  • @ServletComponentScan 등록
  • 등록해야 @WebFilter, @WebListener and @WebServlet 주석을 scan 할 수 있다.
  • 해당 어노테이션은 스프팅 부트의 임베디드 서블릿 컨테이너에서만 작동한다.

 

@Slf4j
//asyncSupported 없으면 에러
@WebFilter(urlPatterns = {"/v1/test/contents/searchlist", "/v1/test/profile/list"}, asyncSupported = true)
//@Configuration //이거있으면 필터두번실행됨
public class FiltersConfig extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException {
        final HttpServletRequest requestWrapper = new RequestWrapper(req);
        filterChain.doFilter(requestWrapper, res);
        //filterChain.doFilter(req, res);
    }
}
  • @ComponentScan을 하면 필터가 두번실행된다.
  • "@WebFiter + @ServletComponentScan" 조합으로 한번 등록이 됬는데 한번더 등록하기 때문이다.
  • Webfilter의 속성인 urlPatterns에 필터를 탈 url을 지정해준다
  • asyncSupported가 없으면 에러가 뜨는데 잘 모르겠다 
  • 따로 만든 HttpServletRequest 클래스에서 실질적으로 가공이 일어난다. 이를 doFilter의 인자로 던져주면 된다.

 

@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    private final ObjectMapper objectMapper;
    //private final String body;
    private byte[] rawData;
    private byte[] newData;
//    private ByteArrayInputStream bis;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.objectMapper = new ObjectMapper();
        StringBuilder stringBuilder = new StringBuilder();
        //objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        try {
            InputStream inputStream = request.getInputStream();
            this.rawData = IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            throw e;
        }

        //요청값 없을때 체크
        try {
            log.info("URL : {}", request.getRequestURI());
            // json 형식으로 유입된 HttpServletRequest를 JSOBObject 형태로 return
            JSONObject jsonObject = MyUtil.readJSONStringFromRequestBody(rawData);
            log.info("jsonObject toString {} ", jsonObject.toString());
            String search = (String) jsonObject.get("search"); //jsonbody의 key값 
            String t_idx = (String) jsonObject.get("t_idx"); //jsonbody의 key값
            jsonObject.put("t_idx", Integer.parseInt(t_idx));
            log.info("search {} ", search);

            if (request.getRequestURI().contains("searchlist")) {
                System.out.println("Gd");
                ContentsListReqDTO _reqDTO = objectMapper.readValue(jsonObject.toString().getBytes(StandardCharsets.UTF_8), ContentsListReqDTO.class);
                if (_reqDTO.getSearch().contains("\'")) {
                    _reqDTO.setSearch(_reqDTO.getSearch().replace("\'", "\\'"));
                    jsonObject.put("search", _reqDTO.getSearch());
                }
            }
            //this.bis = new ByteArrayInputStream(httpRequestBodyByteArray);
            newData = jsonObject.toString().getBytes(StandardCharsets.UTF_8);

            //stringBuilder.append(objectMapper.readValue(jsonObject.toString().getBytes(StandardCharsets.UTF_8), String.class));
        } catch (Exception e) {
            e.printStackTrace();
        }
        //this.body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bis = new ByteArrayInputStream(newData);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return bis.available() == 0;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() {
                return bis.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    @Override
    public ServletRequest getRequest() {
        return super.getRequest();
    }

//    public byte[] convertToByteArrays(Object obj) throws Exception {
//        if (obj == null) return null;
//        ByteArrayOutputStream bos = new ByteArrayOutputStream();
//        ObjectOutputStream oos = new ObjectOutputStream(bos);
//        oos.writeObject(obj);
//        oos.flush();
//        return bos.toByteArray();
//    }

//    public Object convertToObject() throws IOException {
//        if (httpRequestBodyByteArray.length == 0) return null; // body가 비어있더라도 잘 처리하도록..
//        return objectMapper.readValue(httpRequestBodyByteArray, Object.class);
//    }
}
  • 톰캣에서 request body를 한번 읽으면 또 읽지 못하도록 막아놨다. (request.getInputStream() 부분)
  • 한번 읽고 byte[] rawData 여기에 임시저장한다.
  • MyUtil.readJSONStringFromRequestBody()에 rawData를 보내서 key-value 형태를 JSONObject형태로 받는다.
  • 특정 Controller에서 받는 DTO를 가공해야하니 jsoinObject를 해당 DTO로 변환 후 가공처리를 한다.
    • 이부분이 찾기 어려웠는데, objectMapper.readValue()를 써서 첫번째 인자는 byte값, 두번재인자는 클래스 타입을 넘겨주면 되더라
  • 가공된걸 바이트 형태로 변환후 newData에 담는다.
  • getInputStream(), getReader()를 오버라이딩하여 필터에서 newDate를 반환하도록 세팅한다.

 

public class MyUtil {

    // json 형식으로 유입된 HttpServletRequest를 JSOBObject 형태로 return
    public static JSONObject readJSONStringFromRequestBody(byte[] rawData) throws JsonProcessingException {
        StringBuffer json = new StringBuffer();
        ObjectMapper mapper = new ObjectMapper();
        String line = null;
        InputStream is = null;
        try {
            is = new ByteArrayInputStream(rawData);
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            while((line = reader.readLine()) != null) {
                json.append(line);
            }

        }catch(Exception e) {
            e.printStackTrace();
            //log.info("Error reading JSON string: " + e.toString());
        }

        System.out.println(json.toString());
        Map<String, String> map = mapper.readValue(json.toString(), Map.class);

        JSONObject jObj = new JSONObject(map);
        return jObj;
    }
    
}

 

 

 

참조

1. https://taetaetae.github.io/2020/04/06/spring-boot-filter/

 

스프링 부트에 필터를 '조심해서' 사용하는 두 가지 방법

웹 어플리케이션에서 필터를 사용하면 중복으로 처리되는 내용을 한곳에서 처리할 수 있다거나 서비스의 다양한 니즈를 충족시키기에 안성맞춤인 장치인것 같다. 필터란 무엇인가 에 대한 내

taetaetae.github.io

 

2. https://www.baeldung.com/spring-servletcomponentscan

 

저작자표시 (새창열림)

'Tech > Spring' 카테고리의 다른 글

Validation 클래스 단위 제약과 조건부 검사 설정  (0) 2021.12.22
Mybatis와 Jpa 사용시 트랜잭션 묶어서 사용하는 방법(※ 멀티 datasource 설정 / QueryDsl)  (1) 2021.07.24
단위 테스트를위한 ReflectionTestUtils  (1) 2021.03.23
Springboot + GCP Cloud Storage 연동(파일 업로드, 다운로드)  (1) 2021.03.22
Spring Boot에서 Actuator 및 Spring Actuator Admin 설정 방법  (0) 2021.03.21
'Tech/Spring' 카테고리의 다른 글
  • Validation 클래스 단위 제약과 조건부 검사 설정
  • Mybatis와 Jpa 사용시 트랜잭션 묶어서 사용하는 방법(※ 멀티 datasource 설정 / QueryDsl)
  • 단위 테스트를위한 ReflectionTestUtils
  • Springboot + GCP Cloud Storage 연동(파일 업로드, 다운로드)
소프
소프
  • 소프
    기회는 찬스
    소프
  • 전체
    오늘
    어제
    • 분류 전체보기 (138)
      • Language (20)
        • Java (19)
        • Design Pattern (1)
      • Tech (27)
        • Spring (19)
        • JPA (3)
        • QueryDSL (1)
        • Gradle (4)
      • 개발 생각 (1)
      • IT Book (0)
        • 자바_스프링 개발자를 위한 실용주의 프로그래밍 (0)
      • Database (4)
        • Mysql (4)
        • Redis (0)
      • 네트워크 (1)
      • 운영체제 (2)
      • IDE (12)
        • Intellij (9)
        • vscode (2)
        • datagrip (1)
      • 인프라 (11)
        • Docker (2)
        • Jenkins (7)
        • Github Actions (1)
        • GCP (1)
      • 형상관리 (2)
        • gitlab (2)
        • git (0)
      • 외부활동 (44)
        • CS 면접 끝장내기 - 데이터베이스 1기 (5)
        • CS 면접 끝장내기 - 컴퓨터 네트워크 2기 (5)
        • CS 면접 끝장내기 - 자바 2기 (0)
        • TDD, 클린 코드 with Java 17기 (7)
        • ATDD, 클린 코드 with Spring 8기 (6)
        • 루퍼스 2기 (21)
      • 프로젝트 (0)
      • 회고 (6)
      • 기타 (1)
        • 제미니의 개발 실무 (0)
  • 블로그 메뉴

    • 홈
    • Github
    • LinkedIn
    • 방명록
  • 인기 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
소프
Spring Filter에서 request Body 가공하기
상단으로

티스토리툴바