[spring] handler 폴더, api 메소드 만들기, POST맨 사용방법
서블릿 Filter
- 톰캣의 Filter (web.xml)
- Sevlet이 실행되기 전에 먼저 실행됨
- 권한이 있으면 요청을 수락하고, 권한이 없으면 요청을 거부 할수 있게 해줌
-
인증 및 권한에 따라 Servlet 을 실행 및 거부 할수 있게 해줌, 일종의 수문장 역할 (if 문 같이)
- web.xml 을 수정해서 필터 설정을 하거나, 어노테이션을 이용해서 필터 설정을 할수 있음
스프링 Intercept
- 스프링 자체에서 실행하는 기능
- 스프링 컨테이너가 들고 있는 Filter (Intercept)
- 클라이언트의 요청이 Controller로 가기 전에 중간에 요청을 가로채서 검사하는 모듈
- 예를 들어 클라이언트의 요청이 들어왔는데, 로그인을 하지 않아 Session이 생성되지 않았다면 Interceptor가 체크를 하고 로그인 페이지로 돌려보내주게 됨
- HandlerInterceptorAdapter 상속해서 구현
handler/PillFilter.java
package kr.co.test.study.handler;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class PillFilter implements Filter {
/*
스프링 필터 (Filter)
- HTTP 요청과 응답을 변경할 수 있는 재사용가능한 코드
- 여러 개의 필터가 모여 하나의 체인(chain; 또는 사슬)을 형성할 수도 있다. 클라이언트 - 필터1 - 필터2 - 필터3 - 자원
- 애플리케이션의 HTTP 요청 및 응답을 가로채는 데 사용되는 개체. 필터를 사용하여 두 인스턴스에서 두 가지 작업을 수행 할 수 있다.
client의 요청을 가로채어 작업을 수행할 수 있다.
response 되기 전에 가로채어 작업을 수행할 수 있다.
Request/Response의 처리를 chain 하는 것에 대한 이해를 요구
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
/*
doFilter()
- 서블릿에 요청 및 응답이 오면 블록 안에있는 코드 실행
- 필터체인으로 연결하여 준다. 체인의 경우 순서를 지정할 수 있다. 체인의 가장 마지막에는 클라이언트가 요청한 최종 자원이 위치한다.
1. request 파리미터를 이용하여 요청의 필터 작업 수행, reponse 파라미터를 이용하여 응답의 필터 작업 수행
2. 체인의 다음 필터 처리
*/
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "*");
/** addHeader(name, value)
* 주어진 이름과 정수 값으로 응답 헤더를 설정합니다. 헤더가 이미 설정된 경우 새 값이 이전 값을 덮어씁니다.
* <code>containsHeader</code> 메소드는 값을 설정하기 전에 헤더의 존재 여부를 테스트하는 데 사용할 수 있습니다.
*
* @param name - 헤더의 이름
* @param 값 - 할당된 정수 값
* @see #containsHeader
* @see #addIntHeader
*
*
* Access-Control-Allow-Origin : *
*
*/
res.setHeader("Access-Control-Allow-Origin", "*");
/** setHeader(name, value)
* 주어진 이름과 값으로 응답 헤더를 추가합니다. 이 방법을 사용하면 응답 헤더가 여러 값을 가질 수 있습니다.
*
* @param name 헤더의 이름
* @param 값 추가 헤더 값 옥텟 문자열을 포함하는 경우 RFC 2047에 따라 인코딩해야 합니다.
* (http://www.ietf.org/rfc/rfc2047.txt)
* @see #setHeader
*
*/
res.setHeader("Access-Control-Max-Age", "3600");
// Access-Control-Max-Age : 3600
// res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, PATCH, HEAD");
res.setHeader("Access-Control-Allow-Methods", "*");
res.setHeader("Access-Control-Allow-Headers", "*");
// res.setHeader("Access-Control-Allow-Headers", "origin, x-requested-with, content-type, multipart, multipart/form-data, accept, token, version, locale");
log.info("Logging Request {} : {}", req.getMethod(), req.getRequestURI());
chain.doFilter(request, response);
// 3. chaing.doFilter() 요청을 필터링 안하고 다음 Filter에게 넘긴다.
// 다음 필터에게 requset 및 response 를 넘기고, 그 Filter가 실행된 다음에 다시 돌아와서 밑에줄 코드 실행
log.info("Logging Response :{}", res.getContentType());
}
@Override
public void destroy() {}
//필터가 웹 콘테이너에서 삭제될 때 호출
@Override
public void init(FilterConfig fc) throws ServletException {}
//필터를 웹 콘테이너에 생성 후 초기화할 때 호출
}
handler/FrontApiInterCepter.java
package kr.co.test.study.handler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class FrontApiInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
/*
* 컨트롤러(즉 RequestMapping이 선언된 메서드 진입) 실행 직전에 동작.
* 반환 값이 true일 경우 정상적으로 진행이 되고, false일 경우 실행이 멈춥니다.(컨트롤러 진입을 하지 않음)
* 전달인자 중 Object handler는 핸들러 매핑이 찾은 컨트롤러 클래스 객체입니다.
* */
log.info("FrontInterceptor > preHandle");
// TODO 사용 token 체크기능 필요?
if (StringUtils.equals(request.getMethod(), "OPTIONS")) {
log.debug("if request options method is options, return true");
return true;
}
// 받은 request 에 사용된 HTTP 메서드의 이름이 "OPTIONS"이면 Log 띄움
/*
* StringUtils.equlas(parm1, parm2)
* parm1과 parm2 가 같으면 true 리턴
* */
/**
* request.getMethod()
* 이 요청에 사용된 HTTP 메서드의 이름을 반환합니다(예: GET, POST 또는 PUT). CGI 변수 REQUEST_METHOD의 값과 동일합니다.
*
* @return a <code>String</code> 이 요청에 사용된 메서드의 이름을 지정합니다.
*/
/**
* request.getPathInfo()
* 클라이언트가 이 요청을 할 때 보낸 URL과 관련된 추가 경로 정보를 반환합니다. 추가 경로 정보는 서블릿 경로를 따르지만 쿼리 문자열 앞에 있으며 "/" 문자로 시작합니다.
* <p>
* 이 메서드는 추가 경로 정보가 없는 경우 <code>null</code>을 반환합니다.
* <p>
* CGI 변수 PATH_INFO의 값과 동일합니다.
*
* @return a <code>String</code>, 웹 컨테이너에 의해 디코딩되며, 요청 URL의 쿼리 문자열 앞에 있지만 서블릿 경로 뒤에 오는 추가 경로 정보를 지정합니다. 또는 URL에 추가 경로 정보가 없는 경우 <code>null</code>
*/
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info("FrontInterceptor > postHandle");
}
/*
* 컨트롤러 진입 후 view가 랜더링 되기 전 수행이 된다.
* 전달인자의 modelAndView을 통해 화면 단에 들어가는 데이터 등의 조작이 가능하다.
* */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
throws Exception {
log.info("FrontInterceptor > afterCompletion");
}
}
/*
* 컨트롤러 진입 후 view가 정상적으로 랜더링 된 후 제일 마지막에 실행이 되는 메서드
* */
api/controller/back/BackBoardController.java
package kr.co.test.study.api.controller.back;
import io.swagger.annotations.*;
import kr.co.test.study.api.model.common.MailAddrVo;
import kr.co.test.study.api.model.common.PaginationVo;
import kr.co.test.study.api.model.common.RspCode;
import kr.co.test.study.api.model.common.SearchVo;
import kr.co.test.study.api.model.study.BoardVo;
import kr.co.test.study.api.model.study.ManagerVo;
import kr.co.test.study.api.service.BoardService;
import kr.co.test.study.api.util.FileUtil;
import kr.co.test.study.api.util.RestApi;
import kr.co.test.study.settings.ProductConstants;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.method.P;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/*
@RestController - 컨트롤러 클래스에 @RestController를 붙이면, 컨트롤러 클래스 하위 메서드에 @ResponseBody 어노테이션을 붙이지 않아도 문자열과 JSON 등을 전송할 수 있다.
@Controller와 달리 @RestController는 컨트롤러 클래스의 각 메서드마다 @ResponseBody를 추가할 필요가 없어졌다.
Spring 4.3부터 Spring MVC 컨트롤러 메소드를 위해 새로운 어노테이션 5개가 추가됨
@RequestMapping(value="/getList", method = { RequestMethod.POST} -> @PostMapping("/getList") 으로 짧게 줄여쓸수 있음
@api - 클래스를 swagger 리소스 대상으로 표시
*/
@RestController
@RequestMapping("/api/back/board")
@Api(tags = "관리자 문의 게시판")
public class BackBoardController {
@GetMapping("/welcome")
public String welcome(){
return "The server is running well -lee hoogy";
}
}
rest API 서버 만들기
(1). 컨트롤러 클래스 만들기
예제)
package seok.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import seok.model.Todo;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
@RequestMapping(value = "/todo")
public class TodoController {
private final AtomicInteger counter = new AtomicInteger();
@RequestMapping("/todo")
public Todo todo(){
return new Todo(counter.incrementAndGet(), "코딩하기");
}
//AtomicInteger는 Atomic(더 이상 쪼개질 수 없는 성질)을 의미하며, 단순히 Integer 타입으로 선언한다면 서로 다른 Thread 에서 하나의 변수에 대해 값을 쓰거나 읽기 때문에 문제가 발생할 수 있지만, AtomicInteger를 쓰므로 Thread-safe 하게 처리가 가능하다.
//POST 메서드 생성
@PostMapping("/todo")
public Todo registryTodo(@RequestParam(value="todoTitle") String todoTitle){
return new Todo(counter.incrementAndGet(), todoTitle);
}
//RESPONSE 메서드 생성
@PostMapping("/todo/response")
public ResponseEntity<Todo> postRegistryTodo(@RequestParam String todoTitle){
return new ResponseEntity<>
(new Todo(counter.incrementAndGet(), todoTitle), HttpStatus.CREATED);
}
}
(2). 모델 클래스 만들기
- 데이터를 담을 수 있는 클래스 (주고받을 자료구조 클래스, 객체)
예제)
package seok.model;
public class Todo {
private int id;
private String title;
public Todo(int id, String title) {
this.id = id;
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
POST맨 사용방법
- workspace 만든 후 start somting new -> get/post 할수 있는 창 뜸
-
http 메소드 선택후 send -> 응답 확인
- POST (insert, update) 확인하는 방법은 POST카테고리 선택 -> Body -> raw 체크 -> 카테고리 JSON 선택
{
"key1" : "value1",
"key2" : "value2"
}
형식으로 작성후 send