GVKun编程网logo

在Spring 3.0.5中,如何防止参数绑定解释逗号?(spring 参数绑定原理)

14

在本文中,我们将给您介绍关于在Spring3.0.5中,如何防止参数绑定解释逗号?的详细内容,并且为您解答spring参数绑定原理的相关问题,此外,我们还将为您提供关于API接口如何防止参数被篡改和重

在本文中,我们将给您介绍关于在Spring 3.0.5中,如何防止参数绑定解释逗号?的详细内容,并且为您解答spring 参数绑定原理的相关问题,此外,我们还将为您提供关于API接口如何防止参数被篡改和重放攻击?、Spring MVC 参数绑定、Spring MVC 请求参数绑定 例子(转)、Spring MVC–参数绑定常用的注解的知识。

本文目录一览:

在Spring 3.0.5中,如何防止参数绑定解释逗号?(spring 参数绑定原理)

在Spring 3.0.5中,如何防止参数绑定解释逗号?(spring 参数绑定原理)

考虑以下控制器方法:

@RequestMapping(value = "/test", method = RequestMethod.GET)public void test(@RequestParam(value = "fq", required = false) String[] filterQuery) {    logger.debug(fq = " + StringUtils.join(filterQuery, "|"));}

这是不同fq组合的输出:

  1. /test?fq=foo 结果是 fq = foo
  2. /test?fq=foo&fq=bar 结果是 fq = foo|bar
  3. /test?fq=foo,bar 结果是 fq = foo|bar
  4. /test?fq=foo,bar&fq=bash 结果是 fq = foo,bar|bash
  5. /test?fq=foo,bar&fq= 结果是 fq = foo,bar|

示例3是问题所在。我期望(想要/需要)它输出fq = foo,bar

我已经尝试过使用\和转义逗号,%3C但是工作很少。

如果我查看HttpServletRequest对象的版本:

String[] fqs = request.getParameterValues("fq");logger.debug(fqs = " + StringUtils.join(fqs, "|"));

它输出预期的输出:fqs = foo,bar。因此,“问题”在于Spring数据绑定。

我可以绕过Spring的绑定和使用,HttpServletRequest但是我真的不想要,因为我在真实代码中使用了 支持bean
(正在发生相同的事情),并且不想重新实现绑定功能。我希望有人可以提供一种通过转义或其他机制防止这种行为的简单方法。

TIA

更新: 我将此问发布在Twitter上,并得到答复说 预期的输出随Spring 3.0.4.RELEASE一起出现
。我现在已经确认是这种情况,因此是一个临时解决方案。我将继续并将其记录为Spring
JIRA系统上的错误。如果任何人都可以提供解决方法或使用3.0.5进行修复,我将接受他们的回答。

答案1

小编典典

我已经测试了您的代码:令人难以置信,但是我无法重现您的问题。我已经下载了最新版本的spring(3.0.5),这是我的控制器:

package test;import org.apache.commons.lang.StringUtils;import org.apache.log4j.Logger;import org.springframework.stereotype.Controller;import org.springframework.validation.BindingResult;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controller@RequestMapping("/test/**")public class MyController {    private static final Logger logger = Logger.getLogger(MyController.class);    @RequestMapping(value = "/test/params", method = RequestMethod.GET)    public void test(SearchRequestParams requestParams, BindingResult result) {    logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));    }}

这是我的SearchRequestParams类:

package test;public class SearchRequestParams {    private String[] fq;    public String[] getFq() {    return fq;    }    public void setFq(String[] fq) {    this.fq = fq;    }}

这是我简单的spring配置:

<bean id="urlMapping"/><bean/><bean id="viewResolver">    <property name="prefix">        <value>/WEB-INF/jsp/</value>    </property>    <property name="suffix">        <value>.jsp</value>    </property></bean>

我已经在tomcat
7.0.8中测试了我的代码;输入时,我http://localhost:8080/testweb/test/params.htm?fq=foo,bar可以在以下行中读取日志文件:DEBUGfq = foo,bar。我的代码与您的代码有什么区别?难道我做错了什么?我想为您提供帮助,因此,如果您有任何疑问或可以为您做其他测试,那将是一种荣幸。

更新/解决方案
通过您的代码,我重现了该问题;您<mvc:annotation-driven/>在分派器Servlet配置中具有标记,因此您默默使用默认的转换服务,实例FormattingConversionService,其中包含从String到的默认转换器,该转换器String[]使用逗号作为分隔符。您必须使用另一个转换服务Bean,其中包含您自己的从String到的转换器String[]。您应该使用其他分隔符,我选择使用“;”
因为它是查询字符串中常用的分隔符(“?first = 1; second = 2; third = 3”):

import org.springframework.core.convert.converter.Converter;import org.springframework.util.StringUtils;public class CustomStringToArrayConverter implements Converter<String, String[]>{   @Override    public String[] convert(String source) {        return StringUtils.delimitedListToStringArray(source, ";");    }}

然后,您必须在配置中指定此转换服务bean:

<mvc:annotation-driven conversion-service="conversionService" /><bean id="conversionService">    <property name="converters">        <list>            <bean/>        </list>    </property></bean>

该问题已解决,现在您应该检查是否有副作用。我希望您在应用程序中不需要原始的从String到的转换String[](以逗号作为分隔符)。;-)

API接口如何防止参数被篡改和重放攻击?

API接口如何防止参数被篡改和重放攻击?


点击上方☝码猿技术专栏 轻松关注,设为星标!

及时获取有趣有料的技术

作者:巨人大哥

cnblogs.com/jurendage/p/12886352.html

说明:目前所有的系统架构都是采用前后端分离的系统架构,那么就不可能避免的需要服务对外提供API,那么如何保证对外的API的安全呢?

生鲜电商中API接口防止参数篡改和重放攻击

目录

1. 什么是API参数篡改?

说明:API参数篡改就是恶意人通过抓包的方式获取到请求的接口的参数,通过修改相关的参数,达到欺骗服务器的目的,常用的防止篡改的方式是用签名以及加密的方式。关注公众号码猿技术专栏获取更多面试资源

2. 什么是API重发攻击?

说明:API重放攻击: 就是把之前窃听到的数据原封不动的重新发送给接收方.

3,常用的解决的方案

常用的其他业务场景还有:

  • 发送短信接口

  • 支付接口

基于timestamp和nonce的方案

微信支付的接口就是这样做的

timestamp的作用

每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。HTTP请求从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。

一般情况下,从抓包重放请求耗时远远超过了60s,所以此时请求中的timestamp参数已经失效了,如果修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,因为不知道签名秘钥,没有办法生成新的数字签名。

但这种方式的漏洞也是显而易见的,如果在60s之后进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效

nonce的作用

nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同。我们将每次请求的nonce参数存储到一个“集合”中,每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。

nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。

nonce参数作为数字签名的一部分,是无法篡改的,因为不知道签名秘钥,没有办法生成新的数字签名。

这种方式也有很大的问题,那就是存储nonce参数的“集合”会越来越大。

nonce的一次性可以解决timestamp参数60s(防止重放攻击)的问题,timestamp可以解决nonce参数“集合”越来越大的问题。

防篡改、防重放攻击 拦截器(用到了redis)

public class SignAuthInterceptor implements HandlerInterceptor {  
  
    private RedisTemplate<String, String> redisTemplate;  
  
    private String key;  
  
    public SignAuthInterceptor(RedisTemplate<String, String> redisTemplate, String key) {  
        this.redisTemplate = redisTemplate;  
        this.key = key;  
    }  
  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
                             HttpServletResponse response, Object handler)
 throws Exception 
{  
        // 获取时间戳  
        String timestamp = request.getHeader("timestamp");  
        // 获取随机字符串  
        String nonceStr = request.getHeader("nonceStr");  
        // 获取签名  
        String signature = request.getHeader("signature");  
  
        // 判断时间是否大于xx秒(防止重放攻击)  
        long NONCE_STR_TIMEOUT_SECONDS = 60L;  
        if (StrUtil.isEmpty(timestamp) || DateUtil.between(DateUtil.date(Long.parseLong(timestamp) * 1000), DateUtil.date(), DateUnit.SECOND) > NONCE_STR_TIMEOUT_SECONDS) {  
            throw new BusinessException("invalid  timestamp");  
        }  
  
        // 判断该用户的nonceStr参数是否已经在redis中(防止短时间内的重放攻击)  
        Boolean haveNonceStr = redisTemplate.hasKey(nonceStr);  
        if (StrUtil.isEmpty(nonceStr) || Objects.isNull(haveNonceStr) || haveNonceStr) {  
            throw new BusinessException("invalid nonceStr");  
        }  
  
        // 对请求头参数进行签名  
        if (StrUtil.isEmpty(signature) || !Objects.equals(signature, this.signature(timestamp, nonceStr, request))) {  
            throw new BusinessException("invalid signature");  
        }  
  
        // 将本次用户请求的nonceStr参数存到redis中设置xx秒后自动删除  
        redisTemplate.opsForValue().set(nonceStr, nonceStr, NONCE_STR_TIMEOUT_SECONDS, TimeUnit.SECONDS);  
  
        return true;  
    }  
  
    private String signature(String timestamp, String nonceStr, HttpServletRequest request) throws UnsupportedEncodingException {  
        Map<String, Object> params = new HashMap<>(16);  
        Enumeration<String> enumeration = request.getParameterNames();  
        if (enumeration.hasMoreElements()) {  
            String name = enumeration.nextElement();  
            String value = request.getParameter(name);  
            params.put(name, URLEncoder.encode(value, CommonConstants.UTF_8));  
        }  
        String qs = String.format("%s×tamp=%s&nonceStr=%s&key=%s"this.sortQueryParamString(params), timestamp, nonceStr, key);  
        log.info("qs:{}", qs);  
        String sign = SecureUtil.md5(qs).toLowerCase();  
        log.info("sign:{}", sign);  
        return sign;  
    }  
  
    /**  
     * 按照字母顺序进行升序排序  
     *  
     * @param params 请求参数 。注意请求参数中不能包含key  
     * @return 排序后结果  
     */
  
    private String sortQueryParamString(Map<String, Object> params) {  
        List<String> listKeys = Lists.newArrayList(params.keySet());  
        Collections.sort(listKeys);  
        StrBuilder content = StrBuilder.create();  
        for (String param : listKeys) {  
            content.append(param).append("=").append(params.get(param).toString()).append("&");  
        }  
        if (content.length() > 0) {  
            return content.subString(0, content.length() - 1);  
        }  
        return content.toString();  
    }  
}  

总结:做互联网应用,无论是生鲜小程序还是APP,安全永远都是第一位,安全做好了,其他的才能做得更好,当然,信息安全是一个永久的话题,并非通过本文就能够说得清楚的

希望本文可以给大家一点思考与建议。

另外,作者已经完成了两个专栏的文章Mybatis进阶、Spring Boot 进阶 ,已经将专栏文章整理成书,有需要的公众号回复关键词Mybatis 进阶Spring Boot 进阶免费获取。


往期推荐



Java 程序员常用的高效资源工具集合!!!

IntelliJ IDEA 最新15款 神级超级牛逼插件推荐(自用,真的超级牛逼)

程序员必知的几种限流方案~

SpringBoot+JWT整合实现单点登录SSO

涨姿势了!delete后加 limit是个好习惯么?

字节一面:如何保障消息100%投递成功、消息幂等性?


嘿,你在看吗

本文分享自微信公众号 - 码猿技术专栏(oneswholife)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

Spring MVC 参数绑定

Spring MVC 参数绑定

一、参数绑定注解介绍

(1)Spring MVC 中绑定数据的注解介绍

        Spring MVC 中绑定数据的注解如下:

    1. @RequestParam     绑定单个请求数据,既可以是 URL 中的参数,也可以是表单提交的参数或上传的文件。

    2. @PathVariable    绑定 URL 中的参数值

    3. @CookieValue    绑定 Cookie 参数

    4. @RequestHeader    绑定 HTTP 请求头参数

    5. @ModelAttribute    绑定 Model 参数

    6. @SessionAttributes    绑定 Session 参数

    7. @RequestBody    请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

    8. @ResponseBody    将内容或对象作为 HTTP 响应正文返回,使用 @ResponseBody 将会跳过视图处理部分,而是调用适合 HttpMessageConverter,将返回值写入输出流。

    9. @RequestPart    绑定 “multipart/form-data” 参数。

二、参数绑定注解详解

(1)@RequestParam

        此注解用于绑定单个请求数据,既可以是 URL 中的参数,也可以是表单提交的参数或上传的文件。

        它有三个属性:value、defaultValue、required。

        value    用于设置参数名。

        defaultValue    用于对参数设置默认值。

        required    用于设置是否必需值,默认为 true。为 true 时,如果参数为空,会报错。

        demo1-post.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP ''demo1-post.jsp'' starting page</title>
  </head>
  
  <body>
    <form method="post" action="/web/paramBind/demo1">
     <input type="text" name="username" value="zhangsan" />
     <input type="submit" value="提交" />
    </form>
  </body>
</html>

        Controller 部分代码如下所示:

@Controller
@RequestMapping("/paramBind")
public class ParamBindController {

 @RequestMapping(value={"/demo1-post"})
 public ModelAndView demo1(){
  ModelAndView modelAndView= new ModelAndView();
  modelAndView.setViewName("demo1-post");
  
  return modelAndView;
 }

 @RequestMapping(value={"/demo1"})
 public ModelAndView demo1(@RequestParam(value="username") String username,@RequestParam(value="id",defaultValue="1",required=false) Integer id){
  ModelAndView modelAndView= new ModelAndView();
  modelAndView.addObject("username", username);
  modelAndView.addObject("id", id);
  modelAndView.setViewName("demo1-success");

  return modelAndView;
 }

}

        demo1-success.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP ''demo1-success.jsp'' starting page</title>
 </head>
  
  <body>
    用户名为:${username}
    <br />
    Id为:${id}
  </body>
</html>

        操作步骤和结果如下图所示:

(2)@PathVariable

        此注解用于绑定 URL 中的参数值。它只有一个属性值 value。

        Controller 部分代码如下所示:

@Controller
@RequestMapping("/paramBind")
public class ParamBindController {

 @RequestMapping(value={"/demo2/{id}"})
 public ModelAndView demo2(@PathVariable(value="id") Integer id){
  ModelAndView modelAndView= new ModelAndView();
  modelAndView.addObject("id", id);
  modelAndView.setViewName("demo2");
  
  return modelAndView;
 }

}

        demo2.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP ''demo2.jsp'' starting page</title>
  </head>
  
  <body>
    id值为:${id }
  </body>
</html>

        运行结果如下图所示:

        

(3)@CookieValue

        此注解用于绑定 Cookie 参数。

        Controller 部分代码如下所示:

@Controller
@RequestMapping("/paramBind")
public class ParamBindController {
  
// @CookieValue示例
 @RequestMapping(value={"/initCookie"})
 public ModelAndView initCookie(HttpServletResponse response){
  ModelAndView modelAndView= new ModelAndView();
  response.addCookie(new Cookie("coolieId""123456"));
  modelAndView.setViewName("demo3-initCookie");
  
  return modelAndView;
 }
 
 @RequestMapping(value={"/demo3"})
 public ModelAndView demo3(@CookieValue(value="coolieId",defaultValue="",required=false) String coolieId){
  ModelAndView modelAndView= new ModelAndView();
  modelAndView.addObject("coolieId", coolieId);
  modelAndView.setViewName("demo3");
  
  return modelAndView;
 }
}

        在 /paramBind/initCookie 中设置 cookie,在 /paramBind/demo3 中读取 cookie。

        执行结果如下图所示:

        

(4)@RequestHeader

        此注解用于绑定 HTTP 请求头参数。

        Controller 中部分代码如下所示:

@Controller

@RequestMapping("/paramBind")

public class ParamBindController {
 
// @RequestHeader示例
 @RequestMapping(value={"/demo4"})
 public ModelAndView demo4(@RequestHeader(value="User-Agent",defaultValue="",required=false) String userAgent){
  ModelAndView modelAndView= new ModelAndView();
  modelAndView.addObject("userAgent", userAgent);
  modelAndView.setViewName("demo4");

  return modelAndView;
 }

}

        运行结果如下所示:

        

(5)@ModelAttribute

        此注解用于绑定 Model 参数。Model 中数据的作用域是 Requets 级别的,也就是说在一个 Request 请求中是获取不到其它 Request 请求的 Model 的。

        首先创建一个 model,如 User.java,代码如下:

package com.uni2uni.model;
public class User {
 private Integer id;
 private String username;
 private String password;
 public Integer getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getUsername() {
  return username;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
}

        Controller 的部分代码如下所示:

@Controller
@RequestMapping("/paramBind")
public class ParamBindController {
 
// @ModelAttribute示例
 @RequestMapping(value={"/demo5-post"})
 public String demo5post(){
  
  return "demo5-post";
 }
 
 @RequestMapping(value={"/demo5"},method={RequestMethod.POST})
 public String demo5(@ModelAttribute("user") User user){
  
  return "demo5";
 }
}

        demo5-post.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP ''demo1-post.jsp'' starting page</title>
  </head>
  
  <body>
    <form modelAttribute="user" method="post" action="/web/paramBind/demo5">
     <input type="text" name="username" value="zhangsan" />
     <input type="password" name="password" value="111" />
     <input type="submit" value="提交" />
    </form>
  </body>
</html>

        demo5.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title></title>
  </head>
  <body>
    用户名是:${user.username }
    <br/>
    密码是:${user.password }
  </body>
</html>

        操作结果如下图所示:

(6)@SessionAttributes

        此注解用于绑定 Session 参数。与其它注解不同的是,@SessionAttributes (value="sessionId") 是在 Controller 上加的。如下所示:

@Controller
@RequestMapping("/paramBind")
@SessionAttributes(value="sessionId")
public class ParamBindController {
}

        设置 Session 的代码如下所示:

 @RequestMapping(value={"/initSession"})
 public String initSession(HttpServletRequest request){
  request.getSession().setAttribute("sessionId""111111");
  return "demo6-initSession";
 }

        demo6.jsp 代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title></title>
  </head>
  <body>
    Session的值为:${sessionId }
  </body>
</html>

        执行结果如下图所示:

        

(7)@RequestBody

        此注解用于将请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

(8)@ResponseBody 

        将内容或对象作为 HTTP 响应正文返回,使用 @ResponseBody 将会跳过视图处理部分,而是调用适合 HttpMessageConverter,将返回值写入输出流。一般返回 json 或 xml。

        Controller 中部分代码如下所示:

 @RequestMapping(value={"/demo7"})
 public @ResponseBody User demo7(){
  User user= new User();
  user.setId(1);
  user.setUsername("zhangsan");
  user.setPassword("111");
 
  return user;
 }

        返回 Json 如下所示:

{"id":1,"username":"zhangsan","password":"111"}

(9)@RequestPart

        此注解用于绑定 “multipart/data” 参数。

Spring MVC 请求参数绑定 例子(转)

Spring MVC 请求参数绑定 例子(转)

出处:  SpringMVC参数绑定,这篇就够了!

     [Spring MVC] - SpringMVC的各种参数绑定方式

 

SpringMVC参数绑定,简单来说就是将客户端请求的key/value数据绑定到controller方法的形参上,然后就可以在controller中使用该参数了

下面通过5个常用的注解演示下如何进行参数绑定:

1. @PathVariable注解

  @PathVariable 是用来获得请求url中的动态参数的,可以将URL中的变量映射到功能处理方法的参数上,其中URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。

示例代码:
   @ResponseBody
    @RequestMapping("/testUrlPathParam/{param1}/{param2}")
    public void testUrlPathParam(HttpServletRequest request, @PathVariable String param1,
                                 @PathVariable String param2) {
        System.out.println("通过PathVariable获取的参数param1=" + param1);
        System.out.println("通过PathVariable获取的参数param2=" + param2);
    }

Postman发送请求截图:

发送请求截图发送请求截图

输出结果:

通过PathVariable获取的参数param1=1
通过PathVariable获取的参数param2=2

2.@RequestHeader注解

  @RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。

示例代码:
    @ResponseBody
    @RequestMapping("/testHeaderParam")
    public void testHeaderParam(HttpServletRequest request, @RequestHeader String param1) {
        System.out.println("通过RequestHeader获取的参数param1=" + param1);
    }

Postman发送请求截图:

发送请求截图发送请求截图

输出结果:

通过RequestHeader获取的参数param1=abc

3.@CookieValue注解

  @CookieValue 可以把Request header中关于cookie的值绑定到方法的参数上。

示例代码:
    @ResponseBody
    @RequestMapping("/testCookieParam")
    public void testCookieParam(HttpServletRequest request, HttpServletResponse response,
                                  @CookieValue String sessionid) {
        System.out.println("通过CookieValue获取的参数sessionid=" + sessionid);
    }

Postman发送请求截图:

发送请求截图发送请求截图

输出结果:

通过CookieValue获取的参数sessionid=ebef978eef6c46f8a95cc0990d2d360a

4.@RequestParam注解

  @RequestParam注解用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。提交方式为get或post。(Http协议中,form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和multipart/form-data,默认为application/x-www-form-urlencoded);

  @RequestParam注解实质是将Request.getParameter() 中的Key-Value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段,
get方式中queryString的值,和post方式中body data的值都会被Servlet接受到并转化到Request.getParameter()参数集中,所以@RequestParam可以获取的到;

  该注解有三个属性: value、required、defaultValue; value用来指定要传入值的id名称,required用来指示参数是否必录,defaultValue表示参数不传时候的默认值。

示例代码:
    @ResponseBody
    @RequestMapping("/testRequestParam")
    public void testRequestParam(HttpServletRequest request,
                                   @RequestParam(value = "num", required = true, defaultValue = "0") int num) {
        System.out.println("通过RequestParam获取的参数num=" + num);
    }

Postman发送请求截图:

输出结果:

通过RequestParam获取的参数num=10

5.@RequestBody注解

  @RequestBody注解用来处理HttpEntity(请求体)传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据;

  GET请求中,因为没有HttpEntity,所以@RequestBody并不适用;

  POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。

示例代码:
    @ResponseBody
    @RequestMapping("/testRequestBody")
    public void testRequestBody(HttpServletRequest request, @RequestBody String bodyStr){
        System.out.println("通过RequestBody获取的参数bodyStr=" + bodyStr);
    }

Postman发送请求截图:

发送请求截图发送请求截图

代码运行结果:

通过RequestBody获取的参数bodyStr=这是body的内容

 

 


 

SpringMVC的各种参数绑定方式

1. 基本数据类型(以int为例,其他类似):
Controller代码:

@RequestMapping("saysth.do")
    public void test(int count) {
}

表单代码:

<form action="saysth.do" method="post">
    <input name="count" value="10" type="text"/>
    ......
</form>

  表单中input的name值和Controller的参数变量名保持一致,就能完成数据绑定,如果不一致可以使用@RequestParam注解。需要注意的是,如果Controller方法参数中定义的是基本数据类型,但是从页面提交过来的数据为null或者”"的话,会出现数据转换的异常。也就是必须保证表单传递过来的数据不能为null或”",所以,在开发过程中,对可能为空的数据,最好将参数数据类型定义成包装类型,具体参见下面的例子。

 

2. 包装类型(以Integer为例,其他类似):
Controller代码:

@RequestMapping("saysth.do")
public void test(Integer count) {
}

表单代码:

<form action="saysth.do" method="post">
    <input name="count" value="10" type="text"/>
    ......
</form>

  和基本数据类型基本一样,不同之处在于,表单传递过来的数据可以为null或”",以上面代码为例,如果表单中num为”"或者表单中无num这个input,那么,Controller方法参数中的num值则为null。

 

3. 自定义对象类型:
Model代码:

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Controller代码:

@RequestMapping("saysth.do")
public void test(User user) {
}

表单代码:

<form action="saysth.do" method="post">
    <input name="firstName" value="张" type="text"/>
    <input name="lastName" value="三" type="text"/>
    ......
</form>

非常简单,只需将对象的属性名和input的name值一一匹配即可。

 

4. 自定义复合对象类型:
Model代码:

public class ContactInfo {
    private String tel;
    private String address;

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

public class User {
    private String firstName;
    private String lastName;
    private ContactInfo contactInfo;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public ContactInfo getContactInfo() {
        return contactInfo;
    }

    public void setContactInfo(ContactInfo contactInfo) {
        this.contactInfo = contactInfo;
    }

}

Controller代码:

@RequestMapping("saysth.do")
public void test(User user) {
    System.out.println(user.getFirstName());
    System.out.println(user.getLastName());
    System.out.println(user.getContactInfo().getTel());
    System.out.println(user.getContactInfo().getAddress());
}

表单代码:

<form action="saysth.do" method="post">
    <input name="firstName" value="张" /><br>
    <input name="lastName" value="三" /><br>
    <input name="contactInfo.tel" value="13809908909" /><br>
    <input name="contactInfo.address" value="北京海淀" /><br>
    <input type="submit" value="Save" />
</form>

User对象中有ContactInfo属性,Controller中的代码和第3点说的一致,但是,在表单代码中,需要使用“属性名(对象类型的属性).属性名”来命名input的name。

 

5. List绑定:
List需要绑定在对象上,而不能直接写在Controller方法的参数中。
Model代码:

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

public class UserListForm {
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

}

Controller代码:

@RequestMapping("saysth.do")
public void test(UserListForm userForm) {
    for (User user : userForm.getUsers()) {
        System.out.println(user.getFirstName() + " - " + user.getLastName());
    }
}

表单代码:

<form action="saysth.do" method="post">
    <table>
        <thead>
            <tr>
                <th>First Name</th>
                <th>Last Name</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <td colspan="2"><input type="submit" value="Save" /></td>
            </tr>
        </tfoot>
        <tbody>
            <tr>
                <td><input name="users[0].firstName" value="aaa" /></td>
                <td><input name="users[0].lastName" value="bbb" /></td>
            </tr>
            <tr>
                <td><input name="users[1].firstName" value="ccc" /></td>
                <td><input name="users[1].lastName" value="ddd" /></td>
            </tr>
            <tr>
                <td><input name="users[2].firstName" value="eee" /></td>
                <td><input name="users[2].lastName" value="fff" /></td>
            </tr>
        </tbody>
    </table>
</form>

  其实,这和第4点User对象中的contantInfo数据的绑定有点类似,但是这里的UserListForm对象里面的属性被定义成List,而不是普通自定义对象。所以,在表单中需要指定List的下标。值得一提的是,Spring会创建一个以最大下标值为size的List对象,所以,如果表单中有动态添加行、删除行的情况,就需要特别注意,譬如一个表格,用户在使用过程中经过多次删除行、增加行的操作之后,下标值就会与实际大小不一致,这时候,List中的对象,只有在表单中对应有下标的那些才会有值,否则会为null,看个例子:

表单代码:

<form action="saysth.do" method="post">
    <table>
        <thead>
            <tr>
            <th>First Name</th>
            <th>Last Name</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <td colspan="2"><input type="submit" value="Save" /></td>
            </tr>
        </tfoot>
        <tbody>
            <tr>
                <td><input name="users[0].firstName" value="aaa" /></td>
                <td><input name="users[0].lastName" value="bbb" /></td>
            </tr>
            <tr>
                <td><input name="users[1].firstName" value="ccc" /></td>
                <td><input name="users[1].lastName" value="ddd" /></td>
            </tr>
            <tr>
                <td><input name="users[20].firstName" value="eee" /></td>
                <td><input name="users[20].lastName" value="fff" /></td>
            </tr>
        </tbody>
    </table>
</form>

这个时候,Controller中的userForm.getUsers()获取到List的size为21,而且这21个User对象都不会为null,但是,第2到第19的User对象中的firstName和lastName都为null。打印结果:

aaa - bbb
ccc - ddd
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
null - null
eee - fff

 

6. Set绑定:
Set和List类似,也需要绑定在对象上,而不能直接写在Controller方法的参数中。但是,绑定Set数据时,必须先在Set对象中add相应的数量的模型对象。
Model代码:

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

public class UserSetForm {
    private Set<User> users = new HashSet<User>();

    public UserSetForm() {
        users.add(new User());
        users.add(new User());
        users.add(new User());
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

}

Controller代码:

@RequestMapping("saysth.do")
public void test(UserSetForm userForm) {
    for (User user : userForm.getUsers()) {
        System.out.println(user.getFirstName() + " - " + user.getLastName());
    }
}

表单代码:

<form action="saysth.do" method="post">
    <table>
        <thead>
            <tr>
                <th>First Name</th>
                <th>Last Name</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <td colspan="2"><input type="submit" value="Save" /></td>
            </tr>
        </tfoot>
        <tbody>
            <tr>
                <td><input name="users[0].firstName" value="aaa" /></td>
                <td><input name="users[0].lastName" value="bbb" /></td>
            </tr>
            <tr>
                <td><input name="users[1].firstName" value="ccc" /></td>
                <td><input name="users[1].lastName" value="ddd" /></td>
            </tr>
            <tr>
                <td><input name="users[2].firstName" value="eee" /></td>
                <td><input name="users[2].lastName" value="fff" /></td>
            </tr>
        </tbody>
    </table>
</form>

基本和List绑定类似。
需要特别提醒的是,如果最大下标值大于Set的size,则会抛出org.springframework.beans.InvalidPropertyException异常。所以,在使用时有些不便。

 

7. Map绑定:
Map最为灵活,它也需要绑定在对象上,而不能直接写在Controller方法的参数中。
Model代码:

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

public class UserMapForm {
    private Map<String, User> users;

    public Map<String, User> getUsers() {
        return users;
    }

    public void setUsers(Map<String, User> users) {
        this.users = users;
    }

}

Controller代码:

@RequestMapping("saysth.do")
public void test(UserMapForm userForm) {
    for (Map.Entry<String, User> entry : userForm.getUsers().entrySet()) {
        System.out.println(entry.getKey() + ": " + entry.getValue().getFirstName() + " - " +
        entry.getValue().getLastName());
    }
}

表单代码:

<form action="saysth.do" method="post">
    <table>
        <thead>
            <tr>
                <th>First Name</th>
                <th>Last Name</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <td colspan="2"><input type="submit" value="Save" /></td>
            </tr>
        </tfoot>
        <tbody>
            <tr>
                <td><input name="users[''x''].firstName" value="aaa" /></td>
                <td><input name="users[''x''].lastName" value="bbb" /></td>
            </tr>
            <tr>
                <td><input name="users[''y''].firstName" value="ccc" /></td>
                <td><input name="users[''y''].lastName" value="ddd" /></td>
            </tr>
            <tr>
                <td><input name="users[''z''].firstName" value="eee" /></td>
                <td><input name="users[''z''].lastName" value="fff" /></td>
            </tr>
        </tbody>
    </table>
</form>

打印结果:

x: aaa - bbb
y: ccc - ddd
z: eee - fff

 

Spring MVC–参数绑定常用的注解

Spring MVC–参数绑定常用的注解

springMVC–参数绑定常用的注解

简介:

handler method 参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类:(主要讲解常用类型)

A、处理requet uri 部分(这里指uri template中variable,不含queryString部分)的注解:   @PathVariable;

B、处理request header部分的注解:   @RequestHeader, @CookieValue;

C、处理request body部分的注解:@RequestParam,  @RequestBody;

D、处理attribute类型是注解: @SessionAttributes, @ModelAttribute;

 

1、 @PathVariable 

当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。

示例代码

  1. @Controller  

  2. @RequestMapping(“/owners/{ownerId}”)  

  3. public class RelativePathUriTemplateController {  

  4.   

  5.   @RequestMapping(“/pets/{petId}”)  

  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {      

  7.     // implementation omitted  

  8.   }  

  9. }  

上面代码把URI template 中变量 ownerId的值和petId的值,绑定到方法的参数上。若方法参数名称和需要绑定的uri template中变量名称不一致,需要在@PathVariable(“name”)指定uri template中的名称。

2、 @RequestHeader、@CookieValue

@RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。

示例代码:

这是一个Request 的header部分:

  1. Host                    localhost:8080  

  2. Accept                  text/html,application/xhtml+xml,application/xml;q=0.9  

  3. Accept-Language         fr,en-gb;q=0.7,en;q=0.3  

  4. Accept-Encoding         gzip,deflate  

  5. Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7  

  6. Keep-Alive              300  

  1. @RequestMapping(“/displayHeaderInfo.do”)  

  2. public void displayHeaderInfo(@RequestHeader(“Accept-Encoding”) String encoding,  

  3.                               @RequestHeader(“Keep-Alive”long keepAlive)  {  

  4.   

  5.   //…  

  6.   

  7. }  

上面的代码,把request header部分的 Accept-Encoding的值,绑定到参数encoding上了, Keep-Alive header的值绑定到参数keepAlive上。

@CookieValue 可以把Request header中关于cookie的值绑定到方法的参数上。

例如有如下Cookie值:

  1. JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84  

参数绑定的代码:

  1. @RequestMapping(“/displayHeaderInfo.do”)  

  2. public void displayHeaderInfo(@CookieValue(“JSESSIONID”) String cookie)  {  

  3.   

  4.   //…  

  5.   

  6. }  

即把JSESSIONID的值绑定到参数cookie上。

3、@RequestParam, @RequestBody

@RequestParam 

A) 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String–> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;

B)用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;

C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定;

示例代码:

  1. @Controller  

  2. @RequestMapping(“/pets”)  

  3. @SessionAttributes(“pet”)  

  4. public class EditPetForm {  

  5.   

  6.     // …  

  7.   

  8.     @RequestMapping(method = RequestMethod.GET)  

  9.     public String setupForm(@RequestParam(“petId”int petId, ModelMap model) {  

  10.         Pet pet = this.clinic.loadPet(petId);  

  11.         model.addAttribute(“pet”, pet);  

  12.         return “petForm”;  

  13.     }  

  14.   

  15.     // …  

@RequestBody

该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等;

它是通过使用HandlerAdapter 配置的HttpMessageConverters来解析post data body,然后绑定到相应的bean上的。

因为配置有FormHttpMessageConverter,所以也可以用来处理 application/x-www-form-urlencoded的内容,处理完的结果放在一个MultiValueMap<String, String>里,这种情况在某些特殊需求下使用,详情查看FormHttpMessageConverter api;

示例代码:

 @RequestMapping(value = “/something”, method = RequestMethod.PUT)  public void handle(@RequestBody String body, Writer writer) throws IOException {    

    writer.write(body); 

 }  

4、@SessionAttributes, @ModelAttribute

@SessionAttributes:

该注解用来绑定HttpSession中的attribute对象的值,便于在方法中的参数里使用。

该注解有value、types两个属性,可以通过名字和类型指定要使用的attribute 对象;

示例代码:

  1. @Controller  

  2. @RequestMapping(“/editPet.do”)  

  3. @SessionAttributes(“pet”)  

  4. public class EditPetForm {  

  5.     // …  

  6. }  

@ModelAttribute

该注解有两个用法,一个是用于方法上,一个是用于参数上;

用于方法上时:  通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;

用于参数上时: 用来通过名称对应,把相应名称的值绑定到注解的参数bean上;要绑定的值来源于:

A) @SessionAttributes 启用的attribute 对象上;

B) @ModelAttribute 用于方法上时指定的model对象;

C) 上述两种情况都没有时,new一个需要绑定的bean对象,然后把request中按名称对应的方式把值绑定到bean中。

用到方法上@ModelAttribute的示例代码:

  1. // Add one attribute  

  2. // The return value of the method is added to the model under the name “account”  

  3. // You can customize the name via @ModelAttribute(“myAccount”)  

  4.   

  5. @ModelAttribute  

  6. public Account addAccount(@RequestParam String number) {  

  7.     return accountManager.findAccount(number);  

  8. }  

这种方式实际的效果就是在调用@RequestMapping的方法之前,为request对象的model里put(“account”, Account);

用在参数上的@ModelAttribute示例代码:

  1. @RequestMapping(value=“/owners/{ownerId}/pets/{petId}/edit”, method = RequestMethod.POST)  

  2. public String processSubmit(@ModelAttribute Pet pet) {  

  3.      

  4. }  

首先查询 @SessionAttributes有无绑定的Pet对象,若没有则查询@ModelAttribute方法层面上是否绑定了Pet对象,若没有则将URI template中的值按对应的名称绑定到Pet对象的各属性上。


关于在Spring 3.0.5中,如何防止参数绑定解释逗号?spring 参数绑定原理的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于API接口如何防止参数被篡改和重放攻击?、Spring MVC 参数绑定、Spring MVC 请求参数绑定 例子(转)、Spring MVC–参数绑定常用的注解的相关知识,请在本站寻找。

本文标签:

上一篇spring启动时的过滤顺序(spring实现过滤器)

下一篇清单 使用Spring 3 MVC作为表单支持对象,正确的语法?(spring mvc form表单)