# GET 쿼리 파라미터
http://localhost:8080/request-param?username=hello&age=20
# POST HTML Form 전송
POST /request-param ...
content-type: application/x-www-form-urlencoded
username=hello&age=20
요청 파라미터 조회라고도 한다.
HttpServletRequest의 request.getParameter()
GET 쿼리 파라미터든 POST HTML Form이든 둘 다 형식이 같으므로 구분없이 조회할 수 있다.
@Slf4j
@Controller
public class RequestParamController {
@RequestMapping("/request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
log.info("username = {}, age = {}", username, age);
response.getWriter().write("ok");
}
}
2022-03-01 00:10:55.961 INFO 23155 --- [nio-8080-exec-2] h.s.b.request.RequestParamController : username = 도던, age = 33
HTML Form으로 요청 파라미터를 조회한다.
@RequestParam
@Slf4j
@Controller
public class RequestParamController {
// RestController처럼 스트링 값을 뷰가 아니라 문자 그래도 반환하도록 해준다.
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge
) {
log.info("memberName = {}, memberAge = {}", memberName, memberAge);
return "ok";
}
}
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
@RequestParam String username,
@RequestParam int age
) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
}
이름과 변수명이 같다면 생략할 수 있다.
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
}
이름과 변수명이 같고 String, int, Integer 등의 단순한 타입이면 @RequestParam 애너테이션까지 생략할 수 있다.
너무 다 생략하면 과할 수 있으니 애너테이션 정도는 붙여줘서 요청 파라미터를 읽는다고 명확하게 표시해주자.
필수값 지정
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
@RequestParam(required = true) String username,
@RequestParam(required = false) int age
) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
}
# 쿼리 스트링에서 username을 뺐을 때
2022-03-01 13:36:44.587WARN 77398---[nio-8080-exec-4].w.s.m.s.DefaultHandlerExceptionResolver:Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:Required String parameter'username'is not present]
# 쿼리 스트링에서 age를 뺐을 때
java.lang.IllegalStateException:Optional int parameter'age'is present but cannot be translated into a null value due to being declared as a primitive type.
Consider declaring it as object wrapper for the corresponding primitive type.
required 옵션은 기본적으로 true다.
필수값이 아니면 false로 지정한다.
필수값을 넣지 않으면 400 에러가 발생한다.
null이 아니라 ?username=처럼 빈 값이 들어오는 경우라면 ok가 떨어지므로 주의한다.
필수가 아니더라도 primitive type이면 null이 들어갈 수 없으므로 Wrapper 클래스로 바꿔줘야 한다.
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(
@RequestParam(required = true, defaultValue = "guest") String username,
@RequestParam(required = false, defaultValue = "-1") int age
) {
log.info("username = {}, age = {}", username, age);
return "ok";
}
}
required 상관없이 정상 동작하게 하려면 defaultValue를 사용할 수 있다.
빈 문자도 기본값을 적용해준다.
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username = {}, age = {}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
}
파라미터를 한 번에 전부 받고 싶다면 Map을 사용할 수 있다.
@RequestParam Map
값이 확실하게 1개인 경우에 사용한다.
Map(key=value)
@RequestParam MultiValueMap
MultiValueMap(key=[value1, value2, ...])
ex. (key=userIds, value=[id1, id2])
@ModelAttribute
@Controller
class ExampleController {
@RequestMapping
void example(
@RequestParam String username,
@RequestParam int age
) {
HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);
}
}
실제 개발 하면 요청 파라미터를 다시 객체에 넣는 과정을 반복한다.
@ModelAttribute를 사용하면 이 과정을 자동화할 수 있다.
@Data
public class HelloData {
private String username;
private int age;
}
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
}
HelloData라는 객체를 생성한다.
요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾는다.
해당 프로퍼티의 setter를 호출해 파라미터의 값을 바인딩한다.
파라미터가 username이면 setUsername()을 호출해 값을 입력한다.
문자에 숫자가 들어오거나 한다면 BindException이 발생한다.
@ModelAttribute 생략
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
}
애너테이션을 빼도 작동한다.
객체 이름은 상관 없이 그 내부의 프로퍼티 이름만 맞으면 된다.
@RequestParam 또한 생략 가능해 혼란스러우므로 붙여주는 게 좋다.
애너테이션 생략 시 규칙은 다음과 같다.
@RequestParam
String, int, Integer 등 단순한 타입
@ModelAttribute
나머지
argument resolver로 지정해둔 타입 외의 것들
ex. HttpServletResponse 등은 ArgumentResolver가 처리한다.