일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Spring Batch
- Vue 강의
- scrapy
- python
- JDK1.3
- gradle
- MYSQL
- Vue
- Vue 배우기
- 젠킨스
- Spring
- 구글 애드센스 수익
- 미국주식
- 미국 배당주
- Vue 알아보기
- AES256
- 도커
- intelliJ plugin
- 티스토리 광고 수익
- Spring Batch 강의
- spring Annotation
- docker 명령어
- docker
- python 기초
- Python 기본편
- spring boot 시작
- IntelliJ
- apache log4j
- 애드센스 수익
- docker mysql
나만의공간
Spring Boot Project Initializr (Request Get/POST 출력) #5 본문
연재순서
1. Spring Boot Project Initializr (스프링 부트 프로젝트 생성법)
2. Spring Boot Project Initializr (RestFul API 연결)
3. Spring Boot Project Initialzr (Swagger 연결)
4. Spring Boot Project Initializr (Log4j 연결)
5. Spring Boot Project Initializr (Requet Get/Post 출력)
6. 번외 : Spring Boot War 파일 생성, 로컬 Tomcat 뛰우기
7. Srping Boot Project Initializr (GitHub 위치)
Spring Boot를 이용하여 Get/Post값을 전달 받을경우 Spring에 의해 Domain으로 자동 매핑이 됩니다.
자동 매핑중 Spring내부에서 매핑이 안되고 오류가 발생할 경우가 있습니다. 이때 어떤 Param 정보들이 넘어 왔는지 알수가 없어 답답한 경우가 있었습니다.
이에 Spring에 매핑 되기전에 Filter를 이용하여 넘어온 Param값을 로그로 출력하는 기능을 추가 하고자 합니다.
Domain 로그출력 소스는 아래 블로그에서 가져왔습니다.
https://taetaetae.github.io/2019/06/30/controller-common-logging/
Dependency추가 내용
Build.gradle 내용
plugins {
// id 'org.springframework.boot' version '2.6.2'
id 'org.springframework.boot' version '2.5.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'war'
}
group = 'com.study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
//wrapper {
// gradleVersion = "6.8.3"
// distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
//}
war {
enabled = true
webInf { // webInf에 processeResources의 결과 디렉토리를 포함하도록 추가
with {
from("${buildDir}/resources/main")
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.26'
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
// param 정보 출력을 위한 Dependency 추가 내용
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
implementation group: 'commons-lang', name: 'commons-lang', version: '2.6'
implementation group: 'commons-io', name: 'commons-io', version: '2.11.0'
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
implementation group: 'org.apache.httpcomponents', name: 'httpcore', version: '4.4.15'
implementation group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.7'
}
test {
useJUnitPlatform()
}
Filter Class 추가
package com.study.studyTwo.core;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.entity.ContentType;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@Slf4j
@WebFilter(urlPatterns = "/*")
public class ReadableRequestWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
// Do nothing
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ReadableRequestWrapper wrapper = new ReadableRequestWrapper((HttpServletRequest)request);
chain.doFilter(wrapper, response);
}
@Override
public void destroy() {
// Do nothing
}
public class ReadableRequestWrapper extends HttpServletRequestWrapper {
private final Charset encoding;
private byte[] rawData;
private Map<String, String[]> params = new HashMap<>();
public ReadableRequestWrapper(HttpServletRequest request) {
super(request);
this.params.putAll(request.getParameterMap()); // 원래의 파라미터를 저장
String charEncoding = request.getCharacterEncoding(); // 인코딩 설정
this.encoding = StringUtils.isBlank(charEncoding) ? StandardCharsets.UTF_8 : Charset.forName(charEncoding);
try {
InputStream is = request.getInputStream();
this.rawData = IOUtils.toByteArray(is); // InputStream 을 별도로 저장한 다음 getReader() 에서 새 스트림으로 생성
// body 파싱
String collect = this.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
if (StringUtils.isEmpty(collect)) { // body 가 없을경우 로깅 제외
return;
}
if (request.getContentType() != null && request.getContentType().contains(
ContentType.MULTIPART_FORM_DATA.getMimeType())) { // 파일 업로드시 로깅제외
return;
}
JSONParser jsonParser = new JSONParser();
Object parse = jsonParser.parse(collect);
if (parse instanceof JSONArray) {
JSONArray jsonArray = (JSONArray)jsonParser.parse(collect);
setParameter("requestBody", jsonArray.toJSONString());
} else {
if (parse instanceof String) {
setParameter("string", parse.toString().replace("\"", "\\\""));
} else {
JSONObject jsonObject = (JSONObject)jsonParser.parse(collect);
Iterator iterator = jsonObject.keySet().iterator();
while (iterator.hasNext()) {
String key = (String)iterator.next();
setParameter(key, jsonObject.get(key).toString().replace("\"", "\\\""));
}
}
}
} catch (Exception e) {
log.error("ReadableRequestWrapper init error", e);
}
}
@Override
public String getParameter(String name) {
String[] paramArray = getParameterValues(name);
if (paramArray != null && paramArray.length > 0) {
return paramArray[0];
} else {
return null;
}
}
@Override
public Map<String, String[]> getParameterMap() {
return Collections.unmodifiableMap(params);
}
@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(params.keySet());
}
@Override
public String[] getParameterValues(String name) {
String[] result = null;
String[] dummyParamValue = params.get(name);
if (dummyParamValue != null) {
result = new String[dummyParamValue.length];
System.arraycopy(dummyParamValue, 0, result, 0, dummyParamValue.length);
}
return result;
}
public void setParameter(String name, String value) {
String[] param = {value};
setParameter(name, param);
}
public void setParameter(String name, String[] values) {
params.put(name, values);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.rawData);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
// Do nothing
}
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream(), this.encoding));
}
}
}
Param 로그 출력할 Inspet Class추가
package com.study.studyTwo.core;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Component
@Aspect
@Slf4j
public class LogInspect {
@Pointcut("execution(* com.study..*Controller.*(..))") // 이런 패턴이 실행될 경우 수행
public void loggerPointCut() {
}
@Around("loggerPointCut()")
public Object methodLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
Object result = proceedingJoinPoint.proceed();
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); // request 정보를 가져온다.
String controllerName = proceedingJoinPoint.getSignature().getDeclaringType().getSimpleName();
String methodName = proceedingJoinPoint.getSignature().getName();
Map<String, Object> params = new HashMap<>();
try {
params.put("controller", controllerName);
params.put("method", methodName);
params.put("params", getParams(request));
// params.put("log_time", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("log_time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
params.put("request_uri", request.getRequestURI());
params.put("http_method", request.getMethod());
params.put("request_ip",request.getRemoteAddr());
} catch (Exception e) {
log.error("LoggerAspect error", e);
}
log.info("params : {}", params); // param에 담긴 정보들을 한번에 로깅한다.
ObjectMapper om = new ObjectMapper();
log.info("objectMapper : {}",om.writeValueAsString(params));
return result;
} catch (Throwable throwable) {
throw throwable;
}
}
/**
* request 에 담긴 정보를 JSONObject 형태로 반환한다.
* @param request
* @return
*/
private static JSONObject getParams(HttpServletRequest request) {
JSONObject jsonObject = new JSONObject();
Enumeration<String> params = request.getParameterNames();
while (params.hasMoreElements()) {
String param = params.nextElement();
String replaceParam = param.replaceAll("\\.", "-");
jsonObject.put(replaceParam, request.getParameter(param));
}
return jsonObject;
}
}
테스트용 메소드 추가내용
package com.study.studyTwo.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Api(tags={"메인화면 API"})
@Slf4j
public class StudyController {
@GetMapping("/study")
@ApiOperation(value="Study 하자")
public String helloWorld() {
log.info("로그 테스트중입니다.");
return "Hello World Study";
}
@GetMapping("/param/get/test1")
@ApiOperation(value="이름")
public String getTest1(@RequestParam String name) {
log.info("name : {}",name);
return "name is " + name;
}
@PostMapping("/param/post/test1")
@ApiOperation(value="이름")
public String postTest1(@RequestBody String name) {
log.info("name : {}",name);
return "name is " + name;
}
@PostMapping("/param/post/testList")
@ApiOperation(value="이름")
public String postTest1List(@RequestBody List<String> listName) {
log.info("사이즈 크기 : {}",listName.size());
return "사이즈 크기 " + listName.size();
}
}
서버 실행 후 메소드 접근시 노출 되는 로그 정보
swagger 접근 화면
포스트 배열 테스트용 요청시 로그 출력 화면
15:09:41 [INFO ] (StudyController.java:42) 사이즈 크기 : 3 15:09:41 [INFO ] (LogInspect.java:51) params : {request_ip=0:0:0:0:0:0:0:1, controller=StudyController, http_method=POST, method=postTest1List, params={"requestBody":"[\"가나다라\",\"1234\",\"잘나와여 좋아여\"]"}, log_time=2022-01-06 15:09:41, request_uri=/param/post/testList} 15:09:41 [INFO ] (LogInspect.java:54) objectMapper : {"request_ip":"0:0:0:0:0:0:0:1","controller":"StudyController","http_method":"POST","method":"postTest1List","params":{"requestBody":"[\"가나다라\",\"1234\",\"잘나와여 좋아여\"]"},"log_time":"2022-01-06 15:09:41","request_uri":"/param/post/testList"} |
신규로 추가된 메소드에 대한 설명은 참고 사이트를 참고 하셔도 됩니다.
추가적인 설명도 기재 예정입니다.
'IT > Spring' 카테고리의 다른 글
Spring Boot Project Initializr (GitHub 위치) (0) | 2022.01.14 |
---|---|
번외 : Spring Boot War파일 생성, 로컬 Tomcat 뛰우기 (0) | 2022.01.08 |
Spring Boot Project Initializr (log4j 연결) #4 (0) | 2022.01.05 |
Spring Boot Project Initializr (Swagger 연결) #3 (2) | 2022.01.03 |
Spring Boot Project Initializr (RestFul API 연결) #2 (0) | 2022.01.01 |