- Request - Response 흐름
사용자의 요청이 서버에 도착하면, DispatcherServlet
이 해당 요청을 처리한다. 이때, 요청과 응답에 대한 정보는 각각HttpServletRequest
와 HttpServletResponse
에 저장.
Filter
Filter
는 요청과 응답에 대한 전처리와 후처리를 담당.LoggerFilter
는 요청과 응답에 대한 로깅을 수행. 요청과 응답의 헤더 정보, 바디 정보 등을 로그로 남긴다.Interceptor
Interceptor
는 컨트롤러의 실행 전후에 특정 로직을 실행.AuthorizationInterceptor
는 요청 URL의 로그를 남기고, 특정 HTTP 메소드나 리소스에 대한 요청이면 통과시킨다.Controller
실제 비즈니스 로직을 처리하는 부분이다.AccountApiController
는/api/account/me
URL로 요청이 오면 사용자 정보를 반환하는 로직을 수행한다. 이때, 예외가 발생하면ApiException
을 발생시킨다.Exception Handler
발생한 예외를 특정한 방식으로 처리하는 역할.ApiExceptionHandler
는ApiException
이 발생하면 해당 예외에 맞는 HTTP 상태 코드와 에러 설명을 응답으로 반환한다.
@Slf4j
@Component
public class LoggerFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
var req = new ContentCachingRequestWrapper( (HttpServletRequest) request );
var res = new ContentCachingResponseWrapper( (HttpServletResponse) response );
log.info("INIT URI : {}", req.getRequestURI());
chain.doFilter(req, res);
// request 정보
var headerNames = req.getHeaderNames();
var headerValues = new StringBuilder();
headerNames.asIterator().forEachRemaining(headerKey ->{
var headerValue = req.getHeader(headerKey);
// authorization-token : ??? , user-agent : ???
headerValues
.append("[")
.append(headerKey)
.append(" : ")
.append(headerValue)
.append("] ");
});
var requestBody = new String(req.getContentAsByteArray());
var uri = req.getRequestURI();
var method = req.getMethod();
log.info(">>>>> uri : {} , method : {} , header : {} , body : {}", uri, method, headerValues, requestBody);
// response 정보
var responseHeaderValues = new StringBuilder();
res.getHeaderNames().forEach(headerKey ->{
var headerValue = res.getHeader(headerKey);
responseHeaderValues
.append("[")
.append(headerKey)
.append(" : ")
.append(headerValue)
.append("] ");
});
var responseBody = new String(res.getContentAsByteArray());
log.info("<<<<< uri : {} , method : {} , header : {} , body : {}", uri, method, responseHeaderValues, responseBody);
res.copyBodyToResponse();
}
}
@Slf4j
@RequiredArgsConstructor
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Authorization Interceptor url : {}", request.getRequestURI());
// WEB ,chrome 의 경우 GET, POST OPTIONS = pass
if(HttpMethod.OPTIONS.matches(request.getMethod())){
return true;
}
// js. html. png resource 를 요청하는 경우 = pass
if(handler instanceof ResourceHttpRequestHandler){
return true;
}
// TODO header 검증
return true; // 일단 통과 처리
}
}
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/account")
public class AccountApiController {
private final AccountRepository accountRepository;
@GetMapping("/me")
public Api<AccountMeResponse> me(){
var response = AccountMeResponse.builder()
.name("홍길동")
.email("A@gmail.com")
.registeredAt(LocalDateTime.now())
.build();
var str = "안녕하세요";
var age = 0;
try{
Integer.parseInt(str);
}catch (Exception e){
throw new ApiException(ErrorCode.SERVER_ERROR, e , "사용자 Me 호출시 에러 발생");
}
return Api.OK(response);
}
}
@Slf4j
@RestControllerAdvice
@Order(value = Integer.MIN_VALUE) // 최우선처리
public class ApiExceptionHandler {
@ExceptionHandler(value = ApiException.class)
public ResponseEntity<Api<Object>> apiException(ApiException apiException){
log.error("", apiException);
var errorCode = apiException.getErrorCodeIfs();
return ResponseEntity
.status(errorCode.getHttpStatusCode())
.body(Api.ERROR(errorCode, apiException.getErrorDescription()));
}
}
reference : 패스트캠퍼스 시그니처 백엔드 chapter 4
반응형
'Programming > Java, Spring' 카테고리의 다른 글
스프링 캐싱 메커니즘에서 @Cacheable과 @Cacheput 차이점은 ? (0) | 2023.12.13 |
---|---|
Spring Webflux - 배경, 개념 Cpu bound vs I/O Bound, block vs non-block, mvc vs webflux (1) | 2023.10.16 |
Spring에서 argumentResolver를 사용하여 인증 책임 분리하기 (0) | 2023.07.23 |
스프링 프로젝트 API Server Error 처리하기 (0) | 2023.07.09 |
StringBuilder와 String 클래스의 문자열 만드는 효율 차이 (0) | 2023.06.21 |