2020-7-14 스프링 MVC(5) 어노테이션 기반 MVC 및 URI매핑
스프링 MVC 핵심기술
어노테이션 기반 스프링 MVC
- 요청 매핑
- 핸들러메서드
- 모델과 뷰
- 데이터바인더
- 예외처리
- 글로벌 컨트롤러
요청메서드
- GET
- 클라이언트가 서버 리소스를 요청할때 사용, 캐싱할 수 있다, 브라우저에 기록이 남는다, 북마크할 수 있다, 민감한 데이터는 사용하지 마라, idemponent(멱등)
- POST
- 클라이언트가 서버의 리소스를 수정하거나 새로만들때 사용, 서버에 보내는 데이터는 POST Body에 담는다, 캐시할 수 없다, 북마크할 수 없다, 브라우저에 기록 안남는다, 데이터길이 제한이 없다, 멱등하지않다.
- PUT
- URI에 해당하는 데이터를 새로만들거나 수정할 때 사용,
- POST와 다른점은 URI에 대한 의미가 다르다 : POST의 URI는 보내는 데이터를 처리할 리소스를 지정, PUT의 URIs는 보내는 데이터에 해당하는 리소스를 지칭
- 멱등하다
- PATCH
- PUT과 비슷하지만 기존 엔티티와 새 데이터의 차이점만 보낸다. 멱등하지 않다.
- DELETE
- URI에 해당하는 리소스를 삭제할때 사용.
- 스프링 웹 MVC에서 HTTP Method매핑하기
- @RequestMapping(method=RequestMethod.GET)
- @RequestMapping(method=RequestMethod.POST)
- @GetMapping, @PostMapping, @DeleteMapping, @PutMapping
- @RequestMapping을 클래스 레벨에도 할 수 있다.
@Controller
@RequestMapping(method=RequestMethod.GET) // GET요청만 된다.
public class SampleController {
@RequestMapping(value = "/hello", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
@GetMapping({"/hello", "/hi"}) // 여러개의 URI 매핑도 된다.
public String hello() {
return "hello";
}
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void helloTest() throws Exception {
mockMvc.perform(get("/hello"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("hello"));
mockMvc.perform(post("/hello"))
.andDo(print())
.andExpect(status().isOk());
mockMvc.perform(put("/hello"))
.andExpect(status().is4xxClientError());
}
}
- method를 배열로 받을 수 있다. GET, POST만 허용하기때문에 이외의 메서드는 테스트에서 에러가 발생한다
URI 매핑
- @RequestMapping은 여러가지 형태를 지원한다.
- ? : 한글자 (“/author/?” => /author/1 , “/author/????’ => /author/1234”)
- *: 여러글자 (“/author/*” => /author/h /author/123…)
- ** : 여러path (“/author/**” => /author/hello , /author/hello/world)
- 정규식 : /{name:정규식} => “/{name:[a-z]}”
@Controller
@RequestMapping("/hello")
public class SampleController {
@GetMapping("")
public String test() {
return "hello";
}
@RequestMapping("/{name:[a-z]+}")
@ResponseBody
public String hello(@PathVariable String name) {
return "hello " + name;
}
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void pathTest() throws Exception {
mockMvc.perform(get("/hello/jimmy"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("hello jimmy"));
}
}
@Controller
@RequestMapping("/hello")
public class SampleController {
@GetMapping("/jimmy")
@ResponseBody
public String test() {
return "hello jimmy";
}
@GetMapping("/**")
@ResponseBody
public String test2(@PathVariable String name) {
return "hello2 " + name;
}
}
@Test
public void pathTest() throws Exception {
mockMvc.perform(get("/hello/jimmy"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("hello jimmy"))
.andExpect(handler().methodName("test"));
}
- Class레벨에 @RequestMapping을 하면 해당 uri이하로 만들 수 있다.
- 정규표현식과 @PathVariable을 이용해서 받을 수 있다.
- 위의 코드에서 /hello/jimmy를 보내게 되면 hello2 + jimmy가 아닌 hello jimmy가 응답으로 온다. 그 이유는 최대한 명확하게
미디어타입
-
특정한 타입의 데이터를 담고있는 요청만 처리하는 핸들러
-
@RequestMapping(consumes=”application/json”) => MediaType.**를 쓰자
-
Content-Type헤더로 필터링
-
매치되지 않으면 415 Not Supported MediaType응답
-
@GetMapping(value = "/jimmy", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String test() { return "hello jimmy"; } ... @Test public void pathTest() throws Exception { mockMvc.perform(get("/hello/jimmy") .contentType(MediaType.APPLICATION_JSON_VALUE)) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("hello jimmy")) .andExpect(handler().methodName("test")); }
-
-
-
특정한 타입의 응답을 만드는 핸들러
-
@RequestMapping(produces=”application/json”) => MediaType.**를 쓰자
-
Accept헤더로 필터링
-
매치되지 않으면 406에러가 발생.
-
@Controller @RequestMapping("/hello") public class SampleController { @GetMapping(value = "/jimmy", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String test() { return "hello jimmy"; } } @Test public void pathTest() throws Exception { mockMvc.perform(get("/hello/jimmy") .contentType(MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("hello jimmy")) .andExpect(handler().methodName("test")); }
-
-
클래스에 선언한 경우
-
@Controller @RequestMapping(value = "/hello", consumes = MediaType.APPLICATION_XML_VALUE) public class SampleController { @GetMapping(value = "/jimmy", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String test() { return "hello jimmy"; } }
- 위의 경우처럼 class레벨에서 consumes를 정의한 경우와 메서드레벨에서 정의한게 다를경우 메서드 레벨에 있는걸로 오버라이딩한다.
-
Written on July 14, 2020