목적 : 컨텐츠 검색시 싱글쿼터( ' )가 포함된 검색어가 있는경우 \를 붙여주기
마이바티스를 사용할수 밖에 없는 쿼리이며 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/
2. https://www.baeldung.com/spring-servletcomponentscan
'Tech > Spring' 카테고리의 다른 글
Validation 클래스 단위 제약과 조건부 검사 설정 (0) | 2021.12.22 |
---|---|
Mybatis와 Jpa 사용시 트랜잭션 묶어서 사용하는 방법(※ 멀티 datasource 설정 / QueryDsl) (0) | 2021.07.24 |
단위 테스트를위한 ReflectionTestUtils (1) | 2021.03.23 |
Springboot + GCP Cloud Storage 연동(파일 업로드, 다운로드) (0) | 2021.03.22 |
Spring Boot에서 Actuator 및 Spring Actuator Admin 설정 방법 (0) | 2021.03.21 |