RequestMapping 的食用相关详解

@RequestMapping 详解

在 Spring-mvc 中,@RequestMapping 是用来映射请求的,我们知道 Servlet 原始的开发方式中,可以通过 @WebServlet 注解指定该 Servlet 的映射访问地址。而 @RequestMapping 类似也是这样,并且提供了更方便的方式,使得我们在编写 Servlet 更关注业务代码。

源码

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

通过观察源码,我们发现 @RequestMapping 注解可以用在方法、类上(ElementType.METHOD, ElementType.TYPE),header (请求头信息)、params(参数信息)、method(指定提交方式)

详细使用

指定映射地址

  • 在方法上指定,可以通过指定 value 的值,来表示请求的路径,如果请求了对应的路径,就会执行这个被注解的方法(test)

    //表示一个控制器,把 TestController 的实体类交给 Spring 管理。
    @Controller
    public class TestController {
    
        //指定映射地址
        @RequestMapping("/test")
        public String test(){
           	System.out.println("我是测试方法 1 ");
            return "success.jsp";
        }
    }
    

    如果我们把注解定义在方法上,这个方法就是目标地址,那么这个地址的 URL 怎么写?(这里的 helloworld 是项目路径)

    http://localhost:8080/helloworld/test
    
  • 在类上定义

    在上面的例子是,定义在方法上,直接给方法指定请求地址。

    而在类上定义,提供了初步的映射请求URL,表示此请求处理器中所有的目标方法都在此URL下访问

    //表示一个控制器,把 TestController 的实体类交给 Spring 管理。
    @Controller
    @RequestMapping("/demo")
    public class TestController {
        //指定映射地址
        @RequestMapping("/test")
        public String test(){
           	System.out.println("我是测试方法 1 ");
            return "success.jsp";//指向另一个界面给客户端
        }
    }
    

    那么这个地址的 URL 怎么写?(这里的 helloworld 是项目路径)

    http://localhost:8080/helloworld/demo/test
    

    这里的请求的路径就变成了 demo 下的 test 了

  • 总结:

    • 目标方法真实的映射URL信息 = 类定义处映射的URL+方法定义出映射的URL
  • 细节:

    • 不管是类定义出映射的URL还是方法定义出映射的URL都可以省略第一个 \ 符号。

设置请求方式

在通过源码,我们看的注解中,还有一个属性 method,这个是用来设置请求方式的。指定了请求方式的目标方法只能处理指定的请求方式。

如果我们需要指定为 post 请求,可以这样写 @RequestMapping(value="url",method=RequestMethod.POST)

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
    //指定映射地址
    @RequestMapping(value="/test",method=RequestMethod.POST)
    public String test(){
       	System.out.println("我是测试方法 1 ");
        return "success.jsp";//指向另一个界面给客户端
    }
}
  • method 还有其他参数
    • RequestMethod.POST ,表示此处理方法只能够处理 POST 请求
    • RequestMethod.GET,表示此处理方法只能够处理 GET 请求
    • RequestMethod.DELETE,表示此处理方法只能够处理DELETE请求
    • RequestMethod.PUT,表示此处理方法只能够处理PUT请求

请求参数

获取请求参数

在 Servlet 中会通过 request.getParameter (" 参数名 ") 的方式在 Servlet 接收请求参数。那么 Spring MVC 还需要这样?

Spring MVC 对参数的获取进行了封装。使得获取请求的参数非常的方便。

假设这里有一个表单

<form  action="${pageContext.request.contextPath}/helloworld/test1">
        名称:<input type="text" name="name"><br>
        密码:<input type="text" name="password"><br>
        <input type="submit" value="提交"><br>

    </form>

那么怎么获取 name 和 password 的值?Spring MVC 有一个约定

  • 目标方法的参数名称和请求参数的名称一致,就会自动匹配

    所以我们的目标方法可以这样写

    //表示一个控制器,把 TestController 的实体类交给 Spring 管理。
    @Controller
    @RequestMapping("/demo")
    public class TestController {
        //指定映射地址
        @RequestMapping(value="/test",method=RequestMethod.POST)
        public String test(String name,String password){
           	System.out.println("名称:"+name+" 密码:"+password);
            return "success.jsp";//指向另一个界面给客户端
        }
    }
    

    如果参数名称就是不同?怎么办?

  • **没事,我们还可以绑定对应的参数名称,具体就是使用 @RequestParam 注解。指定匹配的请求参数名称。**上面的 html 代码修改一下

<form  action="${pageContext.request.contextPath}/helloworld/test1">
        名称:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        <input type="submit" value="提交"><br>

</form>

这里就修改了两个控件的 name 属性,在 目标方法中,只需这样

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
    //指定映射地址
    @RequestMapping(value="/test",method=RequestMethod.POST)
    public String test(@RequestParam(value="username",required=false) String name,String password){
       	System.out.println("名称:"+name+" 密码:"+password);
        return "success.jsp";//指向另一个界面给客户端
    }
}

这样参数不同也可以传递了。解释一下 @RequestParam 注解

  • value 属性:用来指定表单或者 url 参数中的参数名。

  • required 属性:

    • 值为 true 或 false

    • 值为 true 时候,指定客户端请求时,如果没有带该参数(上面的是 username),那么将会报 400 错误

    • 值为 false 时候,那么当没有该参数时候,会赋值为 null (这里也就建议当参数类型为基本类型时,最好换成包装类)

  • defaultValue 属性:

    • 表示设置默认值,如果设置了默认值,即使 required 为 true,也不会报错。

param 属性

上面的 @RequestParam 可以用来对参数绑定和要求带有哪些参数,对于要求带有哪些参数,可以通过 @RequestMapping 中的 param 属性设置。

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
     //保存
    @RequestMapping(value = "/test1",params = {"name","money"})
    @ResponseBody
    public String test1(@RequestParam("name") String n, String money){
        System.out.println(n);
        System.out.println(money);

        return "成功";
    }
}

这里的 param 有两个请求参数,分别是 name 和 money,如果客户端请求没有这两个,那么回报 400 错误,和上面的一样。下面还有几种情况

  • params = {“name=小王”,“money”}
    • 表示必须带有这两个请求参数,并且 name 的值为 小王
  • params = {“name=!小王”,“money”}
    • 表示必须带有这两个请求参数,并且 name 的值不能为 小王
  • params = {"!name",“money”}
    • 表示带有的请求参数必须有 money,并且不能用 name 的请求参数。

请求头

headers 属性

通过在 @RequestMapping 的 headers 属性指定请求头。因为请求头有多个,可以使用一个数组表示。

例如我这里指定请求头中 Connection 的值必须为 keep-alive ,那么可以如下所示

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
    //保存
    @RequestMapping(value = "/test2",headers = {"Connection=keep-alive"})
    @ResponseBody
    public String test2(String name, String money){
        System.out.println(name);
        System.out.println(money);

        return "成功";
    }
}

如果请求头的 Connection 不是 keep-alive 则会报 404 错误。

@RequestHeader

和上面的不同意思,因 header 参数可以用来设置和现代,而这个注解可以注解在参数上,进行获取头信息。如下:

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
    @RequestMapping(value = "/test3",headers = {"Connection=keep-alive"})
    @ResponseBody
   public String test3(@RequestParam("name") String n, String money,@RequestHeader("Connection") String header){
        System.out.println(n);
        System.out.println(money);
        System.out.println(header);

        return "成功";
    }
}

上面代码中,使用 @RequestHeader 注解 header ,指定 value 值来获取 Connection 的值。

Ant 风格

什么是 Ant 风格?其实也就在映射路径中,可以使用通配符

  • 表示匹配一个字符
  • * 表示匹配任意个字符
  • ** 表示匹配多次路径的任意字符

例如:

//表示一个控制器,把 TestController 的实体类交给 Spring 管理。
@Controller
@RequestMapping("/demo")
public class TestController {
   @RequestMapping(value = "/test4/*/one"})
   @ResponseBody
   public String test4(@RequestParam("name") String n, String money){
        System.out.println(n);
        System.out.println(money);
        return "成功";
    }
}

如上的 @RequestMapping ,我们的请求地址如果是 /demo/test4/aaa/one 或者 /demo/test4/d/one 都能够匹配,也就是只要请求地址是 /demo/test4/任意字符/one 都能够匹配。

同理,如果是 ** ,那么请求地址 /demo/test4/a/b/c/one 也能够匹配,也就是 /demo/test4/任意字符多级路径/one 都能够匹配。

Restful 风格

Restful (Representational state transfer )资源表现层状态转换,是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

我们都知道 http 是无状态协议。所有的状态都存在于服务器。因此客户端想操作服务队,必须通过某种手段,让服务器端发生状态转换,而这种转换建立在表现层,所以叫做资源表现层的状态转换。具体的就是,在 http 协议中

  • GET: 用于获取资源
  • POST: 用于新建资源
  • PUT: 用于更新资源
  • DELETE:用于删除资源

http 协议存在 get 和 post 请求,如果发送 put 和 delete 请求,Spring MVC 可以将 post 请求转换为 put 或 delete 请求。

例子:

  • get 请求查询数据:/user/1 表示查询 id=1(假设这里的 1 指的是 id)的 user 资源
  • post 请求新增数据:/user 表示新增user
  • put 请求修改数据:/user/1 表示修改 id=1(假设这里的 1 指的是 id)的 user 资源
  • delete 请求删除数据:/user/1 表示删除 id=1(假设这里的 1 指的是 id)的 user 资源

那么如何使用?如何把 post 转为为 put 请求和 delete 请求?

先介绍一个注解

@PathVariable

该注解是用来映射 URL 中的参数占位符。Spring 3.0 添加的功能,对于Spring 在 Restful 风格的使用占据了里程碑式的意义。Spring 通过 @PathVariable 对 URL 中的占位符参数绑定到目标方法的形参中。

简单应用:例如我的 url 如下

http://localhost:8080/SSM01_war/demo/user/1

我的 1 是参数,那么对于目标方法怎么写?

@Controller
@RequestMapping("/demo")
public class DemoController {
 @RequestMapping(value="/user/{id}",method = RequestMethod.GET)
    public String selectId(@PathVariable("id") String id){
        System.out.println("查询数据: "+id);
        return "/user/success";
    }
}

只要把映射地址中,需要指定的参数,换成以 / 方式分割参数,对于要绑定的参数写成 {参数名} 即可。然后在目标方法的入参中,用注解 @PathVariable 对形参进行绑定就完成了。

请求地址 http://localhost:8080/SSM01_war/demo/user/1 在请求方式是 get 的情况下,会自动匹配到 /SSM01_war/demo/user/{id},然后执行对应的目标方法。

那么对于如何 post 转 put 和 delete 如何操作呢?

使用

步骤

  1. 配置 HiddenHttpMethodFilter 过滤器
  2. 书写界面
  3. 书写目标方法

配置 HiddenMethodFilter 过滤器

<filter>
	<filter-name>HiddenHttpMethodFilter</filter-name>
	<filter-class>org.springframeword.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>HiddenHttpMethodFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

书写界面

  • get 请求

         <a href="${pageContext.request.contextPath}/demo/user/1"> get 请求	</a>
    
  • post 请求

        <form  action="${pageContext.request.contextPath}/demo/user" method="post">
            <input type="submit" value="post 请求"><br>
        </form>
    
  • put 请求

    	<form action="${pageContext.request.contextPath}/demo/user/1" method="post">
            <input type="hidden" value="put" name="_method">
            <input type="submit" value="put 请求"><br>
        </form>
    
  • delete 请求

        <form  action="${pageContext.request.contextPath}/demo/user/1" method="post">
            <input type="hidden" value="delete" name="_method">
            <input type="submit" value="delete 请求"><br>
        </form>
    

    通过上面的代码,你能发现一点不一样的?没错,就是 put 和 delete 的请求中,都添加了一个 隐藏控件,并且 name 属性都为 _method。

    可以说,过滤器过滤了带有 _method 属性名的空间,如果有属性的 name 为 _method 的话,需要进行判断是否为 put 和 delete 请求。

    查看 HiddenHttpMethodFilter 的源码。(这里就贴部分的,也就是 doFilter 方法,因为是过滤器)

    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";
    ......
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            HttpServletRequest requestToUse = request;
         // 如果是 post 请求
            if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
                // 获取请求参数名为 _method 的 value 值。
                String paramValue = request.getParameter(this.methodParam);
                if (StringUtils.hasLength(paramValue)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, paramValue);
                }
            }
    
            filterChain.doFilter((ServletRequest)requestToUse, response);
        }
    
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
            private final String method;
    
            public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
                super(request);
                // 将请求的值都转为大写
                this.method = method.toUpperCase(Locale.ENGLISH);
            }
    
            public String getMethod() {
                return this.method;
            }
        }
    

    可以得出三个结论

    • 控件必须带有 _method
    • 参数值 put 和 delete 有大小写限制吗?没有,通过上面源观察,我们发现它都是会通过 toUpperCase() 方法来转为大写
    • 一定要使用隐藏控件?
      • 不一定,你可以发现,上面的代码只是通过 getParamete() 方法来获取值,所以可以不用。但是,如果你是显示了控件的话,比如是 text,那么 value 的值就会被用户修改,达不到想要的效果。
  • 书写目标方法

    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @RequestMapping(value="/user/{id}",method = RequestMethod.GET)
        public String selectId(@PathVariable("id") String id){
            System.out.println("查询数据: "+id);
            return "/user/success";
        }
    
        @RequestMapping(value = "/user",method = RequestMethod.POST)
        public String insert(){
            System.out.println("新增数据");
            return "/user/success";
        }
    
        @RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)
        public String update(@PathVariable("id") String id){
            System.out.println("更新数据: "+id);
            return "/user/success";
        }
    
        @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
        public String delete(@PathVariable("id") String id){
            System.out.println("删除数据: "+id);
            return "/user/success";
        }
    }
    

    这里的映射地址都是一样的。唯一不同的就是 method 是不一样的。指定请求方式不同,在对于 put 的时候,Spring MVC 会自动转换。

    请求地址 http://localhost:8080/SSM01_war/demo/user/1 如果请求方式是 get 的话,会自动匹配到 /SSM01_war/demo/user/{id},然后根据对于的请求方式去找对应的目标方法。

@CookValue

使用 @CookieValue 可以获取 Cookie 信息。

	@RequestMapping("testCookie")
	public String testCookie(@CookieValue("JSESSIONID") Object value) {
		System.out.println(value.toString());
		return "success.jsp";
	}

属性

  • value:cook 的属性名称

将ServletAPI作为入参

	@RequestMapping("/testServletAPI")
	public String testServletAPI(HttpServletRequest request,HttpServletResponse response,HttpSession session) {
		
		System.out.println(session.getId());
		
		return SUCCESS;
	}	

获取 POJO 类型参数

Controller 中的业务方法的 POJO 属性名称要与请求参数的name一致, 参数值会自动映射匹配。

http://localhost:8080/user/quick9?username=zhangsan$age=12
    @RequestMapping(value = "/quick12")
    @ResponseBody
    private void save12(User user) throws IOException {
        System.out.println(user);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值