티스토리 뷰

study/springboot

010. Request parameter 처리

까오기 2022. 5. 14. 12:38

Request parameter 관련 정리 내용입니다. 

1. @RequestParam 처리
2. 객체 매핑 
3. @RequestBody 처리 
4. Boolean, Date, DateTime, enum 등 처리 

1. @RequestParam 처리 

@RequestParam은 파라미터에서 하나의 값을 추출할 때 이용이 됩니다. 

String id = (String)request.getParameter("id"); 

이렇게 값을 받을 수 있는데 이런 부분을 쉽게 처리할 수 있도록 해주는 어노테이션입니다. 

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {

    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;

    String defaultValue() default ValueConstants.DEFAULT_NONE;

}

이 어노테이션은 파라미터에서 사용할 수 있으며 기본 사용 형태는 아래와 같습니다. 

사용자가 "id"라는 값으로 요청을 보내는 경우의 예입니다. 

@RequestParam String id
@RequestParam("id") String id
@RequestParam(name="id") String id

userId를 보내지 않는 경우 에러가 발생합니다. 이런 경우 required 옵션을 줄 수 있습니다. 

그외 defautl option을 줄 수 있고 받아 주는 쪽에서 Optional를 이용할 수 있습니다. 

@RequestParam(required = false) final String id
@RequestParam(defaultValue = "none") final String id
@RequestParam final Optional<String> id)

2. 객체 매핑 

form 형태로 전달되는 파라미터는 객체로 쉽게 매핑할 수 있습니다. 

public ResponseEntity<RequestParams01> requestParamObject(final RequestParams01 params){
    ...
}

이 형태는 PathVariable을 매핑할 때도 동일한 형태입니다. 

@PostMapping("/params/{id}")

이렇게 하고 파라미터로 "id"를 전달한다면 우선순위는 파라미터에 있습니다.  <- 중요 

3. @RequestBody 처리 

Form 형태가 아닌 Reuest Body에 값을 전달하는 경우 @RequestBody로 받을 수 있습니다.

Request 요청시 put method로 요청시에는 request body로만 전달해야 합니다. put method가 아니어도 단순 key=value 형태가 아닌 복잡한 구조의의 데이터는 json 형태로 전달하기 때문에 자주 사용하는 어노테이션입니다. 

4. Boolean, Date, DateTime, enum 등 처리

boolean 값이 true인 경우 : "true"," "on", "yes", "1"

boolean 값이 false인 경우 : "false"," "off", "no", "0"

요청을 받는 쪽 타입이 Boolean인 경우 위의 값에 따라 자동 매핑됩니다. 

 

Date, DateTime, Number 등은 스프링에서 DateTimeFormat, NumberFormat을 기본 제공합니다. 

 

enum은 기본적으로 동일 name인 경우 매핑이 되지만 정의되지 않은 값에 대한 예외처리, 기본값 설정 등은 Converter등을 통해서 처리 가능합니다. 

 

예제 코드 

@Slf4j
@RestController
@RequestMapping("/controller/request")
public class RequestParam01APIController {

    /**
     * id가 필수가 아닌 경우 처리 : required = false 
     * @return
     */
    @GetMapping("/requiredfalse")
    public ResponseEntity<String> requestParamRequiredFalse(@RequestParam(required = false) final String id) {
        log.debug("requestParamRequiredFalse call : "+id);
        return ResponseEntity.ok(id);
    }

    /**
     * id가 필수가 아닌 경우 처리 : optional  
     * @return
     */
    @GetMapping("/optional")
    public ResponseEntity<String> requestParamOptional(@RequestParam final Optional<String> id) {
        log.debug("requestParamOptional call : "+id);
        return ResponseEntity.ok(id.orElse(null));
    }

    /**
     * id가 필수가 아닌 경우 처리 : defaultValue  
     * @return
     */
    @GetMapping("/default")
    public ResponseEntity<String> requestParamDefault(@RequestParam(defaultValue = "none") final String id) {
        
        log.debug("requestParamDefault call : "+id+":"+(id==null));
        return ResponseEntity.ok(id);
    }

    /**
     * date type 형식 지정 형태
     * request level에서 convert 처리 
     * @return
     */
    @PostMapping("/date")
    public ResponseEntity<String> requestParamDate(@RequestParam("param") @DateTimeFormat(pattern = "yyyy-MM-dd") final Date date) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");  
        String strDate = dateFormat.format(date);  
        log.debug("requestParamDate call : "+strDate);
        return ResponseEntity.ok(strDate);
    }

    @PostMapping("/long")
    public ResponseEntity<Long> requestParamLong(@RequestParam("param") @NumberFormat(pattern = "#,###,###,###.###") final Long pLong) {
        log.debug("requestParamLong call : "+pLong);
        return ResponseEntity.ok(pLong);
    }

    @PostMapping("/double")
    public ResponseEntity<Double> requestParamDouble(@RequestParam("param") @NumberFormat(pattern = "#,###,###,###.###") final Double pDouble) {
        log.debug("requestParamDouble call : "+pDouble);
        return ResponseEntity.ok(pDouble);
    }

    @PostMapping("/boolean")
    public ResponseEntity<Boolean> requestParamBoolean(@RequestParam("param") final Boolean pBoolean) {
        log.debug("requestParamBoolean call : "+pBoolean);
        return ResponseEntity.ok(pBoolean);
    }
    
    /**
     * request parameters를 객체에서 받고 date 타입은 formate 지정을 한다.   
     * request level에서 convert 처리 
     * @return
     */
    @PostMapping("/params")
    public ResponseEntity<RequestParams01> requestParamObject(final RequestParams01 params) {
        log.debug("requestParamObject call : "+params.toString());
        return ResponseEntity.ok(params);
    }
    
    /**
     * request parameters를 객체에서 받고 date 타입은 formate 지정을 한다.   
     * request level에서 convert 처리 
     * @return
     */
    @PostMapping("/body")
    public ResponseEntity<RequestParams01> requestParamBody(@RequestBody final RequestParams01 params) {
        log.debug("requestParamBody call : "+params.toString());
        return ResponseEntity.ok(params);
    }

    /**
     * PathVariable과 @RequestBody 예제 
     * @param params
     * @param path
     * @return
     */
    @PostMapping("/body/{id}")
    public ResponseEntity<RequestParams01> requestParamBodyWithId(@RequestBody final RequestParams01 params, final RequestParams01 path) {
        log.debug("requestParamBodyWithId call : "+params.toString());
        log.debug("requestParamBodyWithId call : "+path.toString());
        return ResponseEntity.ok(params);
    }
    
    /**
     * pathvariable과 request parameters를 객체에 받을 때 같은 이름으로 요청했을 때 parameter 값이 우선한다.    
     * request level에서 convert 처리 
     * @return
     */
    @PostMapping("/params/{id}")
    public ResponseEntity<RequestParams01> requestParamAndPath(final RequestParams01 params) {
        log.debug("requestParamAndPath call : "+params.toString());
        return ResponseEntity.ok(params);
    }
    
    @Getter
    @Setter
    @ToString
    public static class RequestParams01 {
        
        private String id;
        private String name;
        private Boolean test;
        
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        private Date created;
        
        public void setCreated(Long timestamp) {
            if(timestamp == null) return;
            this.created = new Date(timestamp); 
        }

        public RequestParams01() {
            super();
        }

        public RequestParams01(String id, String name, boolean test, Date created) {
            super();
            this.id = id;
            this.name = name;
            this.test = test;
            this.created = created;
        }
    }

}

테스트 

@WebMvcTest(controllers = RequestParam01APIController.class)
class RequestParam01APIControllerTest {

    @Autowired 
    private MockMvc mockMvc;

    private RequestParams01 getRequestParams01() {
        return new RequestParams01("test", "테스트", true, DateUtil.parseDate("2022-05-12"));
    }

    /**
     * @RequestParam 테스트  
     * @throws Exception
     */
    @ParameterizedTest
    @CsvSource({
        "/controller/request/requiredfalse,,", "/controller/request/requiredfalse,test,test"
        ,"/controller/request/optional,,", "/controller/request/optional,test,test"
        ,"/controller/request/default,,none", "/controller/request/default,test,test"
        })
    void requiredfalse(String uri, String id, String expected) throws Exception {
        MockHttpServletRequestBuilder mrbuilder = MockMvcRequestBuilders.get(uri).contentType(MediaType.APPLICATION_JSON);
        if(id != null) mrbuilder.param("id", id);
        if(expected == null) expected = "";
        MvcResult result = mockMvc.perform(mrbuilder)
                .andDo(print())
                .andExpect(status().isOk())
                .andReturn();
        assertThat(result.getResponse().getContentAsString()).isEqualTo(expected);
    }

    /**
     * 타입 매핑 테스트  
     * @throws Exception
     */
    @ParameterizedTest
    @CsvSource(value = {
        "/controller/request/date#2022-05-12#2022-05-12"
        ,"/controller/request/long#12,000.12#12000"
        ,"/controller/request/double#12,000.12#12000.12"
        ,"/controller/request/boolean#on#true"
        }, delimiterString="#")
    void typeMapping(String uri, String param, String expected) throws Exception {
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(uri)
                .param("param", param)
                .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andReturn();
        assertThat(result.getResponse().getContentAsString()).isEqualTo(expected);
    }
    
    /**
     * 객체 매핑 테스트   
     * @throws Exception
     */
    @Test
    void objectMapping() throws Exception {
        RequestParams01 params = getRequestParams01();
        MockHttpServletRequestBuilder mrbuilder = MockMvcRequestBuilders.post("/controller/request/params").accept(MediaType.APPLICATION_JSON)
                .param("id", params.getId())
                .param("name", params.getName())
                .param("created", DateUtil.formatDate(params.getCreated()))
                .param("test", (params.getTest())? "yes":"no")
                .contentType(MediaType.APPLICATION_JSON);
        execute(mrbuilder, params);
    }
    
    /**
     * @RequestBody 테스트    
     * @throws Exception
     */
    @Test
    void bodyMapping() throws Exception {
        RequestParams01 params = getRequestParams01();
        ObjectMapper objectMapper = new ObjectMapper();
        String body = objectMapper.writeValueAsString(params);
        MockHttpServletRequestBuilder mrbuilder = MockMvcRequestBuilders.post("/controller/request/body")
                .contentType(MediaType.APPLICATION_JSON)
                .content(body);
        execute(mrbuilder, params);
    }

    private void execute(MockHttpServletRequestBuilder mrbuilder, RequestParams01 params) throws Exception {
        mockMvc.perform(mrbuilder)
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(params.getName()))
                .andExpect(jsonPath("$.id").value(params.getId()));
    }

    /**
     * Path와 Parameter에 동일 필드를 이용하는 경우 우선 순위 테스트     
     * @throws Exception
     */
    @Test
    void requestParamAndPath() throws Exception {
        RequestParams01 params = getRequestParams01();
        MockHttpServletRequestBuilder mrbuilder = MockMvcRequestBuilders.post("/controller/request/params/pathId").accept(MediaType.APPLICATION_JSON)
                .param("id", params.getId())
                .param("name", params.getName())
                .param("created", DateUtil.formatDate(params.getCreated()))
                .param("test", (params.getTest())? "yes":"no")
                .contentType(MediaType.APPLICATION_JSON);
        execute(mrbuilder, params);
    }

}

git : https://github.com/kkaok/study-springboot/blob/main/src/main/java/eblo/study/springboot/controller/RequestParam01APIController.java

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함