AJAXJS MVC 使用教程(中)

Posted sp42a

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AJAXJS MVC 使用教程(中)相关的知识,希望对你有一定的参考价值。

处理请求参数

HTTP是文本协议,传递到服务端的请求参数最初之类型皆是字符串。Java中通过request的getParameter()方法返回字符串类型的请求参数,可那实在太简陋了,故任何一款MVC框架都力求提供强大的参数绑定功能,将网页参数自动转化为Java可直接读取的对象而不再是普通的字符串。应该采取什么策略来转换Java里面可读的类型呢?首先可明确一点,请求参数出现的地方将变成控制器的方法的输入参数,即每个Java参数映射着HTTP请求参数;然后控制器方法执行后返回的结果就是响应客户端的结果。

设计一个控制器方法前,应当明确Controller需要对页面传过来的HTTP参数做哪些操作?是GET的QueryString参数还是POST的表单参数,抑或其他?其次是转换后的目标类型,页面传来的参数和Model是一个什么关系?还有与Java参数本身的类型的关系。Controller对返回给页面的数据又要做哪些操作?返回给页面的数据又和Model是什么关系?还有参数是否非空、默认值等的问题,这些都有制定规则才约束实现。

对此MVC框架应该有自己的建模,映射着HTTP协议到Java。类似于Spring MVC,我们的框架当前提供了多种参数数据传输途径:1、原生请求响应对象;2、QueryParameter查询参数;3、FormParameters表单参数;4、PathVariables:路径变量;5、Head:HTTP头部的参数。作为参数的结果,提供了int/long/boolean/Map/Bean的数据载体。既然任由开发者自主设计控制器方法,这些参数都是不定项,可以在同一个方法的不同参数中各自组合,自然都取决于开发者如何定义个这个控制器方法的参数列表。在应用过程中我们大量使用了注解来指定MVC规则。如何应用呢?下面我们逐一说明。

原生请求响应对象

原生对象指的是HttpServletRequest、HttpServletResponse这类由Servlet提供的接口,开发者一定非常熟悉。只要方法定义了这些接口类型的参数,即可送入,不管参数名具体如何。

@GET
public String get(HttpServletRequest request, HttpServletResponse response) {
	HttpSession session = request.getSession();// 原生方法
	ServletContext context = request.getServletContext();   
	return "/my.jsp";
}

只有HttpServletRequest、HttpServletResponse可以直接声明在参数中得到,其他如 HttpSession、ServletContext可通过request/response返回。

为满足其他额外的需求有必要实现新的实用工具函数。我们封装在继承的MvcRequest、MvcOutput对象中。MvcRequest、MvcOutput不是代替request、response,而是在它们基础上扩展,将会在第4.2.5小节中深入介绍。

获取查询参数

查询参数(QueryString)指的URL上的参数,类似请求http://localhost:8080/news/getDetail?pageNo=1&limit=5的地址中,参数部分指的是问号后面的部分,它是键对值结构,多个参数通过&分割,例如参数pageNo的值为1,参数limit的值为5。如果要获取查询参数及其值需要用到@QueryParam注解,它会将请求参数绑定到方法参数。

@GET
@Path("getDetail")
public String gotoJSP(@QueryParam("pageNo")String pageNo, @QueryParam("limit")String limit) {
    return "html:: pageNo= " + pageNo;
}

需要注意的是,获取查询参数的注解不一定限于HTTP GET请求,其他类型请求的方法也可以出现@QueryParam。它与HTTP方法没有必然联系,只是获取QueryString时多为GET请求,比如说当为POST请求时候亦能读取QueryParam的参数。

值和非空的参数

在配置请求参数时可以指定丰富的选项,例如是否非空、参数的默认值。通过指定@DefaultValue注解定义默认值,表示当客户端传过来的参数值不存在或者为空时就会采用这个默认值,如下例子中设置了默认name值为Jack。

@POST
@Path("testDefault")
public String testDefault(@DefaultValue("Jack") @QueryParam("name") String name) {
	return "json::{\\"name\\":\\"" + name + "\\"}";
}

如果想把参数设置为必填的,可声明一个@NotNull的注解。下面例子演示了非空的判断,若name为空会打印相关错误信息,如“java.lang.NullPointerException: 客户端缺少提交的参数 name”。

@POST
@Path("testDefault")
public String testDefault(@NotNull @QueryParam("name")String name, @QueryParam("age") int age) {
	return "json::{\\"name\\":\\"" + name + "\\"}";
}

@DefaultValue@NotNull适用于各种HTTP方法注解(@GET/@POST等)。

自动类型转换

HTTP请求过来都是String,如果可根据方法参数类型作适当的转换,那样就省去了开发者手动转换的麻烦。如下例增加的获取年龄的参数,最终希望获取的是一个int类型的值,只要我们声明方法是声明int age而不是String age,那么框架就会自动帮助我们尽量地尝试转换,当然输入如“20A”含有字符串Java肯定抛出不能转换的异常。

@POST
@Path("testDefault")
public String testDefault(@DefaultValue("Jack") @QueryParam("name") String name, @QueryParam("age") int age) {
	return "json::{\\"age\\":\\"" + age + "\\"}";
}

当前支持输入的目标类型有String、int/Integer、long/Long、boolean/Boolean,具体对应转换后的Java类型如表格 4.3例子所示。
表格 4.3: 转换真实类型的例子

在这里插入图片描述

小结一下,像是否允许非空、参数的默认值和自动类型转换的这些特性(如下表格 4.4所示)在不同HTTP方法中获取参数的时候也是通用的。

在这里插入图片描述

请求路经获取参数

RESTful(RESTful指通过标准的HTTP(GET/POST/PUT/DELETE)操作来构建基于面向资源的API接口,是获取资源的一种方式。现在主流Web框架都支持RESTful。)的概念使得URL发生了变化,其中一特点是将Path路径参数化,URL 的目录可作为特定含义的参数。比如有一个用户id查询查询详情,通过上面介绍的方式我们可以这么做:

@GET
@Path("getUserDetailInfo")
public String getUserDetailInfo(@QueryParam("userId")long userId) {
    return "userInfo";
}

那么客户的的请求路径应是这样:/getUserDetailInfo?userId=12345,尽管也可以满足需求,却不太符合RESTful的理念。理想的情况是,资源应该是由请求路径决定的,而不是由请求参数决定。或者说,请求参数不应该被用来描述一个资源。/user/12345这种请求方式显然比/getUserDetailInfo?userId=12345更合适,前者定义了要查询的资源,而后者更强调了通过参数进行操作。

为了达到RESTful风格的控制器这个目标,MVC允许请求路径中包含一个占位符,占位符名称需要用一对尖括号{id}括起来。在客户的请求资源时,请求路径的其它部分还是用来匹配资源路径,而占位符部分则直接用来传输参数值。下面就是通过占位符的方式实现路径中传递参数值:

@GET
@Path("getUserDetailInfo/{id}")
public String getUserDetailInfo(@PathParam("id")long userId) {
    return "userInfo";
}

@Path@PathParam可以看出,{id}的值会传递到方法的userId参数中。比如按照上面的配置,如果我们的请求是/getUserDetailInfo/12345,则123456便会传入变成userId的值。需要注意的是,当前id的值只能转换为数字类型的(int/long)。

在请求的数据量很小的时候使用查询参数和路径参数还是可行的,但是有时候请求的数据量会很大,再用上面两种方式就显得有点不太合适,这时就需要考虑用第三种方式:表单参数。

处理表单数据(转换为Map或Bean)

一般POST提交数据就是提交表单数据,它比查询参数提供能够携带更多的数据。假设HTML表单如下代码。

<h1>会员注册</h1>
        <form method="POST">
           用户名: <input type="text" name="username" /><br/>
  	年龄: <input type="text" name="age" /><br/>
           密码: <input type="password" name="password" /><br/>
           <input type="submit" value="注册" />
 </form>

表单都是以POST方式提交的,所以这个控制器对应@POST注解,执行下面几种方法接收表单数据。

@POST
@Path("/map")
public String map(Map<String, Object> map) { // 使用Map装载请求数据
	int age = (int)map.get("age");// 
	return "html::年龄是: " + map.get("age");
}

使用Map装载请求数据的同时还尝试转换字符串为真实值的类型,所谓真实类似是根据字符串尝试转化为Java各种数据类型,最后保存在Map对象<String, Object>中(其中String是参数名,Object是参数值)。

实际上所有的HTTP参数名称和参数值本身都保存在Map对象。转为Map时调用了原生Servlet的request.getParameterMap()方法。

除了Map类型的数据对象,很多时候使用Bean装载请求数据,能不能直接从请求数据自动转为Bean呢?答案是肯定的。如下例bean()方法还有一个含有User对象的参数,这个User对象中含有name、age、password 属性,MVC会根据名称从请求中解析相同名称参数并赋值到对象属性中,组装好一个实体。

@POST
@Path("/bean")
public String bean(User user) {
	System.out.println(user .getName());
	return "html::" + user.getName();
}

注意Bean必须是BaseModel的子类,因为它是判断Object是否为Bean的一个必要条件。

验证表单

在SpringMVC3.0以后便支持Java Validation API,提供了一些注解来约束对象属性值,这些注解有我们可以利用这些注解给User对象添加一些验证,比如非空和字符串长度验证:

public class User {
    @NotNull
    @Size(min=3,max=20)
    private String name;
    @NotNull
    @Size(min=6,max=20)
    private String password;
    @NotNull
    @Size(min=3,max=20)
    private String accountNo;
}

我们还需要通过@Valid标签在方法中对User启用参数验证:

@RequestMapping(value="/register", method=RequestMethod.POST)
public String processRegistration(@Valid User user, Errors errors) {
    if(errors.hasErrors()){
        return "register";
    }
    registerService.register(user);
    return "redirect:/user/" + user.getName();
}

在上面例子中,我们在方法中的User参数前面添加了@valid注解,这个注解会告诉框架需要启用对这个对象的验证。如果发现任何验证错误,错误信息都将会被封装到Errors对象中,我们可以通过errors.hasErrors()方法判断是否验证通过。

以上是关于AJAXJS MVC 使用教程(中)的主要内容,如果未能解决你的问题,请参考以下文章

AJAXJS MVC 使用教程(下)

学写一个 Java Web MVC 框架

Spring MVC 3.2 Thymeleaf Ajax 片段

ASP.net MVC 代码片段问题中的 Jqgrid 实现

Spring MVC 教程

VIM 代码片段插件 ultisnips 使用教程