@RequestMapping Method Argument
@RequestMapping Method Arguments
Controller에서 정의된 @RequestMapping 메소드
즉, 요청을 핸들링하는 메소드는 여러가지 매개변수를 받을 수 있습니다.
다음 내용은 @RequestMapping 메소드의 매개변수에
명시할 수있는 Annotation과 Spring에서 자동으로 매개변수에 주입가능한 타입에 대한 정리입니다.
Spring.io doc 를 참고해서 정리하였습니다.
불명확한 부분은 테스트하여 정리하였습니다.
<br/>
Request Parameter 이해하기
우선 Request Parameter를 정의는 다음과 같습니다.
- URL 에 붙은 Query String
- Submitt시에 Form Data
<br/>
Type Conversion
기본적으로 String-based로 하는 input에 대한 Annotation은 타입변환을 자동적으로 지원 합니다.
ex.
@RequestParam,@RequestHeader,@PathVariable,@MatrixVariable,@CookieValue
String이 아닌 다른 타입으로 자동으로 전환되어 받을 수 있음을 의미합니다.
예를들면 http://test.com?value=1의 요청에 @RequestParam int value로 타입변환하여 받을 수 있습니다.
<br/>
No Annotation, No Inject
어노테이션을 명시하지 않거나, Spring이 자동으로 주입할수 없는 타입의 매개변수인 경우,
매개변수가 SimpleProperty이면, @RequestParam으로 자동 명시됩니다.
매개변수가 SimplePerperty아니면, @ModelAttribute로 자동 명시됩니다.
예를들어, DTO 객체로 받거나, 기본타입으로 매개변수를 받는경우에는
Annotation 없어도 스프링이 자동으로 Annotation을 명시하여 사용하는 것입니다. (실제로 명시는 안하지만)
SimpleProperty?simple value type: a primitive or primitive wrapper, an enum, a String or other CharSequence, a Number, a Date, a Temporal, a URI, a URL, a Locale, or a Class.
BeanUtils#isSimpleProperty호출로 알 수 있습니다.
간단히하면, 기본타입과 기본타입의 wrapper 클래스는true입니다.
DTO객체는false입니다.
<br/>
@RequestParam
Request Parameter(query string, form data)에 매핑하도록 하는 Annotation 입니다.
- DTO객체로 매핑이 안되고, 각 파라미터를 각자의 매개변수로 받아야합니다.
- 또는
Map<String, String>,MultiValueMap<String, String>으로 한번에 받을 수 있습니다.
다음 속성 값을 가집니다.
value: =namename: 매핑될 파라미터명을 정의합니다, 없으면 매개변수명으로 파라미터를 찾아 바인딩합니다.required: 기본값은true이며,true일 때 파라미터가 없으면400(Bad Request)을 클라이언트에게 반환합니다.defaultValue: 기본값을 지정합니다. 지정하게되면required가 자동으로false로 바뀝니다.
//default value 설정
@GetMapping("/")
public String endpoint(@RequestParam(value = "param", defaultValue = "abc") String param){
System.out.println(param);
return "";
}
// 쿼리 파라미터
//GET http://localhost:8080/?paramA=abc¶mB=123
@GetMapping("/")
public String endpoint(@RequestParam String paramA, @RequestParam String paramB){
return "";
}
// 쿼리 파라미터, Map으로받기
//GET http://localhost:8080/?paramA=abc¶mB=123
@GetMapping("/")
public String endpoint(@RequestParam Map<String, String> params){
return "";
}
// form 형식
// POST http://localhost:8080/ form paramA=abc, paramB=123
@PostMapping("/")
public String endpoint(@RequestParam String paramA, @RequestParam String paramB){
return "";
}
<br/>
@ModelAttribute
매개변수에 Request Parameter, Path Variable, Model, SessiaonAttributes를 매핑합니다
명시된 메소드 매개변수, 명시된 메소드의 반환값을 Model에 자동으로 추가합니다.
@ModelAttribute를 선언하면Model에 자동으로 추가됩니다.Controller의 메소드에도 명시할 수 있으며, 모든 요청에 해당 메소드의 반환값이Model에 추가됩니다.Request Parameter를 DTO객체에 매핑 할 수 있습니다.setter또는, 멤버변수를 포함하는 생성자를 필요로합니다.- 없으면, 주입되지 않아 객체 멤버변수가
null입니다.
@Valid어노테이션을 추가하면, 유효성 검사를 실행합니다.BindingResult를 매개변수로 바로 다음 매개변수로 받으면, 예외가 발생하지 않습니다.BindingResult를 매개변수로 받지 않거나 다음 위치의 매개변수가 아니면,400(Bad Request)을 반환합니다.
<br/>
@ModelAttribute가 명시된 dto객체는 다음과 같이 값이 주입됩니다.
@GetMapping("/{paramA}")
public String processSubmit(@ModelAttribute Dto dto) { }
private static class Dto{
String paramA;
}
Model에 이미 추가되어있는 있는지 확인하고, 있는 경우dto에 주입합니다.@SessionAttributes를 통해서session에 동일한 값이 있는지 확인하고 주입합니다.Path Variable의 값에 매핑되는dto프로퍼티가 있을 경우, 자동으로 주입됩니다.dto프로퍼티가Request Parameter와 매칭되는 생성자를 호출하여 인스턴스를 생성합니다.- 기본생성자를 호출하여 인스턴스를 생성합니다. 생성 후 파라미터와 일치한 프로퍼티의
setter를 통해 값이 주입됩니다.
<br/>
다음 속성 값을 가집니다.
value: = namename: Model에 주입시 Atrribute namebinding: 기본값true입니다.false일경우Request Parametet데이터 바인딩을 하지 않습니다. 즉,setter를 통한 주입을하지 않습니다. 생성자로는 주입 가능합니다(!)
public class MainController{
// MainController의 모든 요청에, 메소드 반환값이 `Model`에 주입된다.
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
}
@GetMapping("/")
public String processSubmit(@ModelAttribute Dto dto) {
}
<br/>
@RequestHeader
Request Header를 어노테이션을 통해 메소드 매개변수로 받을 수 있습니다.
- 각 매개변수에 각
@RequestHeader에 매핑됩니다. - 또한,
Map<String, String>,MultiValueMap<String, String>,HttpHeaders형태로 복수로 받을 수 있습니다.
다음 속성값을 가집니다.
value: =namename: 매핑될Header명을 정의합니다. 대소문자를 구분하지 않습니다. 없으면 매개변수명으로 매핑됩니다.(컨벤션 변경은 안됩니다content-type->contentType불가)required: 기본값으로true이기 때문에, 파라미터가 없으면400(Bad Request)을 클라이언트에게 반환합니다.defaultValue: 기본값을 지정합니다. 지정하게되면required가 자동으로false로 바뀝니다.
@GetMapping("/demo")
public void handle(@RequestHeader("Accept-Encoding") String encoding) {
//...
}
<br/>
@CookieValue
Cookie 값에 매핑됩니다.
다음 속성값을 가집니다.
value: =namename: 매핑될Cookie키값을 정의합니다. 없으면 매개변수명으로 매핑됩니다.required: 기본값으로true이기 때문에, 파라미터가 없으면400(Bad Request)을 클라이언트에게 반환합니다.defaultValue: 기본값을 지정합니다. 지정하게되면required가 자동으로false로 바뀝니다.
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//...
}
<br/>
@RequestBody
매개변수가 요청의 body에 바인딩되어 변환됩니다.
body값은HttpMessageConverter에 의해서 변환되는데, 매개변수 타입에 의해 변환됩니다.- DTO객체로 받을 수 있습니다.
- DTO에 기본생성자가 있어야 합니다. 접근지정자는 상관없습니다.
- 기본생성자가 없으면 500(Internal Server Error)을 반환합니다.
setter를 필요로 하지 않습니다.
@Valid어노테이션을 추가하면 유효성 검사가 이 적용됩니다.BindingResult를 매개변수로 바로 다음 매개변수로 받으면, 예외가 발생하지 않습니다.BindingResult를 매개변수로 받지 않으면,MethodArgumentNotValidException예외가 발생하며,400을 반환합니다.
다음 속성 값을 가집니다.
required: required true이기 때문에, 없을 경우 클라이언트에게 400을 던진다.
// content-type을 어떤것으로 보내든 body를 String으로 받을 수 있다.
@PostMapping("/")
public String endpointB(@RequestBody String param){
System.out.println(param);
return "";
}
// content-type을 application/json 으로 보내 DTO 객체로 받는다.
@PostMapping("/")
public String endpointB(@RequestBody Dto param){
System.out.println(param);
return "";
}
@ToString
private static class Dto{
String param;
}
// content-type: text/plain으로 보냈지만, 매개변수가 DTO객체이기 때문에 400예외가 발생한다.
@PostMapping("/")
public String endpointB(@RequestBody Dto param){
System.out.println(param);
return "";
}
@ToString
private static class Dto{
String param;
}
//Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported]
<br/>
HttpEntity<T>
body, header 값을 가지고 있습니다.
- 제네릭 타입으로 정해진 타입으로
body가 변환됩니다. @Valid를 사용하여 유효성 검사를 할 수 없습니다.
@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
<br/>
Custom Resolver 정의하기
Spring이 지원외에도, 자동으로 객체를 주입하도록 커스텀하게 정의할 수 있습니다.
// CustomHandlerMethodArgumentResolver
public class CustomHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(CustomDto.class); // CustomDto가 매개변수로 있는경우
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return new CustomDto("A", "B"); // 해당 객체를 주입
}
}
// WebConfig, Resolver 설정
@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CustomHandlerMethodArgumentResolver());
}
}
@GetMapping("/")
public String endpointB(CustomDto customDto){
System.out.println(customDto); // == CustomDto("A", "B")
return "";
}
<br/>
etc
그밖에 Spring 으로부터 주입받을 수 있는 매개변수 타입입니다.
<br/>
HttpSession
HttpSession은 HttpServletRequest#getSession(true) 의 반환값으로, null이 주입 될 수 없습니다.
HttpSession은 쓰레드세이프 하지않음을 주의해야합니다.
RequestMappingHandlerAdapter#synchronizeOnSession를 true로 함으로써 쓰레드세이프하게 할 수 있습니다.
<br/>
HttpMethod
요청의 HttpMethod를 가져옵니다.
<br/>
Principal
현재 인증된(authenticated) 사용자 java.security.Principal을 가져옵니다.
<br/>
@MatrixVariable
matrix parameter를 받는 Annotation입니다.
사용한 것을 보지 못해, 간단한 예제를 첨부합니다.
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
<br/>
@RequestAttribute
Request Attribute를 명시하여 가져옵니다.
name 속성을 지정하지 않으면, 파라미터명의 Request Attribute를 가져옵니다.
requirted 기본값이 true입니다.
<br/>
@SessionAttributes, SessionStatus
model attribute에 추가된 값을 session attribute에도 추가하는 값입니다.
Controller클래스 단에,
@SessionAttributes에 지정된 attribute key값은
model attribute추가와 동시에 session attribute에도 추가됩니다.
sessionStatus#setComplete()를 호출하여, attribute를 초기화 할 수 있습니다.
@Controller
@SessionAttributes("dto") // session attribute에 자동으로 추가될 `key`값 지정.
public class SampleController {
@GetMapping("/")
public String hello(@ModelAttribute Dto dto) {
// @SessionAttributes("dto") 코드에 의해, 동시에 Session Attribute에도 추가됩니다.
}
}
@Controller
@SessionAttributes("dto")
public class SampleController {
@GetMapping("/")
public String hello(SessionStatus sessionStatus) {
// session attribute를 초기화 합니다.
sessionStatus.setComplete();
}
}
<br/>
@SessionAttribute
Session Attribute를 명시하여 가져옵니다.
name 속성을 지정하지 않으면, 파라미터명의 Session Attribute를 가져옵니다.
requirted 기본값이 true입니다.
<br/>
Model, ModelMap, Map
Model을 가져옵니다.
<br/>
Locale
현재 요청의 Locale 정보를 가져옵니다.
<br/>
ServletRequest, ServletResponse
request, response를 가져옵니다.
HttpServletRequest, MultipartRequest, MultipartHttpServletRequest 도 가능합니다.
<br/>
UriComponentsBuilder
UriComponentsBuilder가 주입됩니다.
주입된 uriComponentsBuilder.buid() 호출 시 현재 요청된 도메인주소를 확인 할 수 있습니다.