Manual Dalam Talian YMP / 控制器参数(Parameter)

控制器参数(Parameter)

WebMVC模块不但让编写控制器变得非常简单,处理请求参数也变得更加容易!WebMVC会根据控制器方法参数或类成员的注解配置,自动转换与方法参数或类成员对应的数据类型,参数的绑定涉及以下注解:

基本参数注解
  • @RequestParam:绑定请求中的参数;

  • @RequestHeader:绑定请求头中的参数变量;

  • @CookieVariable:绑定Cookie中的参数变量;

上面三个注解拥有相同的参数:

value:参数名称,若未指定则默认采用方法参数变量名;

prefix:参数名称前缀,默认为"";

defaultValue:指定参数的默认值,默认为"";

示例代码:

    @Controller
    @RequestMapping("/demo")
    public class DemoController {

        @RequestMapping("/param")
        public IView testParam(@RequestParam String name,
                          @RequestParam(defaultValue = "18") Integer age,
                          @RequestParam(value = "name", prefix = "user") String username,
                          @RequestHeader(defaultValue = "BASIC") String authType,
                          @CookieVariable(defaultValue = "false") Boolean isLogin) {

            System.out.println("AuthType: " + authType);
            System.out.println("IsLogin: " + isLogin);
            return View.textView("Hi, " + name + ", UserName: " + username + ", Age: " + age);
        }
    }

通过浏览器访问URL测试:

    http://localhost:8080/demo/param?name=webmvc&user.name=ymper

执行结果:

    控制台输出:
    AuthType: BASIC
    IsLogin: false

    浏览器输出:
    Hi, webmvc, UserName: ymper, Age: 18
特别的参数注解
  • @PathVariable:绑定请求映射中的路径参数变量;

    value:参数名称,若未指定则默认采用方法参数变量名;

    示例代码:

    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @RequestMapping("/path/{name}/{age}")
        public IView testPath(@PathVariable String name,
                          @PathVariable(value = "age") Integer age,
                          @RequestParam(prefix = "user") String sex) {
    
            return View.textView("Hi, " + name + ", Age: " + age + ", Sex: " + sex);
        }
    }

    通过浏览器访问URL测试:

    http://localhost:8080/demo/path/webmvc/20?user.sex=F

    执行结果:

    Hi, webmvc, Age: 20, Sex: F

    注意:基于路径的参数变量必须是连续的,如:

    • 正确:/path/{name}/{age}

    • 错误:/path/{name}/age/{sex}

  • @ModelBind:值对象参数绑定注解;

    prefix:绑定的参数名称前缀,可选参数,默认为"";

    示例代码:

    public class DemoVO {
    
        @PathVariable
        private String name;
    
        @RequestParam
        private String sex;
    
        @RequestParam(prefix = "ext")
        private Integer age;
    
        // 省略Get和Set方法
    }
    
    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @RequestMapping("/bind/{demo.name}")
        public IView testBind(@ModelBind(prefix = "demo") DemoVO vo) {
            String _str = "Hi, " + vo.getName() + ", Age: " + vo.getAge() + ", Sex: " + vo.getSex();
            return View.textView(_str);
        }
    }

    通过浏览器访问URL测试:

    http://localhost:8080/demo/bind/webmvc?demo.sex=F&demo.ext.age=20

    执行结果:

    Hi, webmvc, Age: 20, Sex: F
  • @ParameterEscape:控制器方法参数转义注解;

    可以通过WebMVC模块配置参数parameter_escape_order设定是在控制器方法参数执行验证之前还是之后执行参数转义动作,参数取值范围为beforeafter,默认为after即参数验证之后进行转义;

    scope:字符串参数转义范围,默认为Type.EscapeScope.DEFAULT;

    • 取值范围包括:JAVA, JS, HTML, XML, SQL, CSV, DEFAULT;
    • 默认值DEFAULT,它完成了对SQL和HTML两项转义;

    skiped:通知父级注解当前方法或参数的转义操作将被忽略,默认为false;

    processor:自定义字符串参数转义处理器;

    • 可以通过IParameterEscapeProcessor接口实现自定义的转义逻辑;
    • 默认实现为DefaultParameterEscapeProcessor;

    示例代码一:

    @Controller
    @RequestMapping("/demo")
    @ParameterEscape
    public class DemoController {
    
        @RequestMapping("/escape")
        public IView testEscape(@RequestParam String content,
                                @ParameterEscape(skiped = true) @RequestParam String desc) {
    
            System.out.println("Content: " + content);
            System.out.println("Desc: " + desc);
            return View.nullView();
        }
    }
    
    // 或者:(两段代码执行结果相同)
    
    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @RequestMapping("/escape")
        @ParameterEscape
        public IView testEscape(@RequestParam String content,
                                @ParameterEscape(skiped = true) @RequestParam String desc) {
    
            System.out.println("Content: " + content);
            System.out.println("Desc: " + desc);
            return View.nullView();
        }
    }

    通过浏览器访问URL测试:

    http://localhost:8080/demo/escape?content=<p>content$<br><script>alert("hello");</script></p>&desc=<script>alert("hello");</script>

    执行结果:(控制台输出)

    Content: &lt;p&gt;content$&lt;br&gt;&lt;script&gt;alert(&quot;hello&quot;);&lt;/script&gt;&lt;/p&gt;
    Desc: <script>alert("hello");</script>

    示例一说明:

    • 由于控制器类被声明了@ParameterEscape注解,代表整个控制器类中所有的请求参数都需要被转义,因此参数content的内容被成功转义;
    • 由于参数desc声明的@ParameterEscape注解中skiped值被设置为true,表示跳过上级设置,因此参数内容未被转义;

    示例代码二:

    @Controller
    @RequestMapping("/demo")
    @ParameterEscape
    public class DemoController {
    
        @RequestMapping("/escape2")
        @ParameterEscape(skiped = true)
        public IView testEscape2(@RequestParam String content,
                                @ParameterEscape @RequestParam String desc) {
    
            System.out.println("Content: " + content);
            System.out.println("Desc: " + desc);
            return View.nullView();
        }
    }

    通过浏览器访问URL测试:

    http://localhost:8080/demo/escape2?content=<p>content$<br><script>alert("hello");</script></p>&desc=<script>alert("hello");</script>

    执行结果:(控制台输出)

    Content: <p>content$<br><script>alert("hello");</script></p>
    Desc: &lt;script&gt;alert(&quot;hello&quot;);&lt;/script&gt;

    示例二说明:

    • 虽然控制器类被声明了@ParameterEscape注解,但控制器方法通过skiped设置跳过转义,这表示被声明的方法参数内容不进行转义操作,因此参数content的内容未被转义;
    • 由于参数desc声明了@ParameterEscape注解,表示该参数需要转义,因此参数内容被成功转义;

    注意:当控制器类和方法都声明了@ParameterEscape注解时,则类上声明的注解将视为无效;

非单例控制器的特殊用法

单例控制器与非单例控制器的区别:

  • 单例控制器类在WebMVC模块初始化时就已经实例化;
  • 非单例控制器类则是在每次接收到请求时都将创建实例对象,请求结束后该实例对象被释放;

基于以上描述,非单例控制器是可以通过类成员来接收请求参数,示例代码如下:

@Controller(singleton = false)
@RequestMapping("/demo")
public class DemoController {

    @RequestParam
    private String content;

    @RequestMapping("/sayHi")
    public IView sayHi(@RequestParam String name) {
        return View.textView("Hi, " + name + ", Content: " + content);
    }
}

通过浏览器访问URL测试:

http://localhost:8080/demo/sayHi?name=YMPer&content=Welcome!

此示例代码的执行结果:

Hi, YMPer, Content: Welcome!

注意:在单例模式下,WebMVC模块将忽略为控制器类成员赋值,同时也建议在单例模式下不要使用成员变量做为参数,在并发多线程环境下会发生意想不到的问题!!