Spring/Spring MVC

[Spring] HTTP 요청 맵핑 - URI 패턴 맵핑

TheWing 2020. 11. 15. 16:18

URL(Uniform Resource Locator 또는 web address, 문화어: 파일식별자, 유일자원지시기)

  • 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 규약이다. 즉, 컴퓨터 네트워크와 검색 메커니즘에서의 위치를 지정하는, 웹 리소스에 대한 참조이다. 쉽게 말해서, 웹 페이지를 찾기위한 주소를 말한다. 흔히 웹 사이트 주소로 알고 있지만, URL은 웹 사이트 주소뿐만 아니라 컴퓨터 네트워크상의 자원을 모두 나타낼 수 있다. 그 주소에 접속하려면 해당 URL에 맞는 프로토콜을 알아야 하고, 그와 동일한 프로토콜로 접속해야 한다.

URI

Uniform Resource Identifier. resource의 식별자이다. 리소스의 식별은 리소스의 위치를 표시하거나 unique한 이름으로 접근할 수 있을 것이다. 현실 세계에 비유하면, 나를 내 거주지(위치)나 나의 주민등록번호로 다른 사람과 나를 구별할 수 있는 객관적인 방법이 생기는 것이다.

URN

Unifrom Resource Name. resource의 name을 나타낸다.예시로는 ISBN 시스템이 있다. URL과 같이 access할 수 있는 위치가 아닌 이름으로 표시된다.

예시: ISBN 0-486-27557-4

URL, URI와 URN의 관계

URL과 URN은 URI의 부분집합이다. rfc 규칙들 중에서 모든 url을 uri로 인정하지 않는 규칙도 있으나 통상적으로 모든 url을 uri로 인정한다.

요청 식별자로 맵핑하기

  • @RequestMapping은 다음의 패턴을 지원합니다.
  • ?: 한 글자 (“/author/???” => “/author/123”)
  • _: 여러 글자 (“/author/_” => “/author/keesun”)
  • : 여러 패스 (“/author/ => “/author/keesun/book”)

클래스에 선언한 @RequestMapping과 조합

  • 클래스에 선언한 URI 패턴뒤에 이어 붙여서 맵핑합니다.

정규 표현식으로 맵핑할 수도 있습니다.

  • /{name:정규식}
@Controller
@RequestMapping("/hello")
public class SampleController {

    @RequestMapping("/{name:[a-z]+}")
    @ResponseBody 
    public String hello(@PathVariable String name) {
        return "hello "+name; 
    }

}
@WebMvcTest
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void helloTest() throws Exception {
        mockMvc.perform(get("/hello/sungjun"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello sungjun"))
                ;

    }

}

패턴이 중복되는 경우에는?

  • 가장 구체적으로 맵핑되는 핸들러를 선택합니다.
@Controller
@RequestMapping("/hello")
public class SampleController {

        @RequestMapping("/sungjun")
    @ResponseBody
    public String helloSungjun() {
        return "hello sungjun";
    }

    @RequestMapping("/**")
    @ResponseBody
    public String hello() {
        return "hello";
    }

}
  • helloSungjun에 맵핑이된다
@Test
public void helloTest() throws Exception {
    mockMvc.perform(get("/hello/sungjun"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string("hello sungjun"))
            .andExpect(handler().handlerType(SampleController.class))
            .andExpect(handler().methodName("helloSungjun"))
            ;

}
  • handler().handlerType(SampleController.class
  • handler().methodName("helloSungjun")

URI 확장자 맵핑 지원

  • 이 기능은 권장하지 않습니다. (스프링 부트에서는 기본으로 이 기능을 사용하지 않도록 설정 해 줌)
    • 보안 이슈 (RFD Attack)
    • URI 변수, Path 매개변수, URI 인코딩을 사용할 때 할 때 불명확 함.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/hello")
public class SampleController {

        @RequestMapping("/sungjun") //1
    @ResponseBody
    public String helloSungjun() {
        return "hello sungjun";
    }

    @RequestMapping({"/sungjun", "/sungjun.*"}) //2
    @ResponseBody
    public String helloSungjun() {
        return "hello sungjun";
    }

    @RequestMapping("/**")
    @ResponseBody
    public String hello() {
        return "hello";
    }

}
  • 1번인 것을 2번으로 암묵적으로 시행해주는데 스프링 부트에서는 지원을 해주지 않는다. (권장사항이 아님)
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

@WebMvcTest
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void helloTest() throws Exception {
        mockMvc.perform(get("/hello/sungjun.json"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello sungjun"))
                .andExpect(handler().handlerType(SampleController.class))
                .andExpect(handler().methodName("helloSungjun"))
                ;

    }

}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/hello")
public class SampleController {

    @RequestMapping("/sungjun")
    @ResponseBody
    public String helloSungjun() {
        return "hello sungjun";
    }
}
  • /hello/sungjun.json 이렇게 요청하면 404 에러가 발생한다

RFD Attack

Reference