此次接着上次的博客继续将springMVC控制器的东西说完。本篇主要说说控制器处理带属性参数的url请求的三种方式:参数风格、rest风格、传统的HttpServlet风格。css
参数风格html
其实,上篇博客已经在示例当中将参数风格的实现方式给出了,不过没有详细说明。java
所谓参数风格,就是讲url的请求参数按照url请求参数的格式予以呈现,咳咳,彷佛有点废话,不过这种方式应该是最通常的方式,也是过去一直用的。web
好比,请求 http://localhost:8080/mvc/courses/view?courseId=123 spring
控制器写法:(完整写法借鉴上一篇博客,或者在本文最后一并给出新的)数据库
//提供完成一个业务的方法:根据课程ID查询课程内容。 //本方法将处理 /courses/view?courseId=123 形式的URL @RequestMapping(value="/view", method= RequestMethod.GET) public String viewCourse(@RequestParam("courseId") Integer courseId, Model model) { //日志输出,查看请求的courseId是否是咱们的courseId log.info("In viewCourse, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); model.addAttribute(course); return "course_overview"; }
这里,参数风格的处理方式,须要接收url中的参数,需要借助@RequestPara注解。@RequestPara注解在方法的某个参数变量上,而后注解@RequestPara的注解value设置为url中参数值,这样@RequestPara就创建起了url的参数与方法参数之间的映射,也就完成了参数的传递。好比,这里的请求 http://localhost:8080/mvc/courses/view?courseId=123中的请求参数courseId与方法参数上注解@RequestParam("courseId")中的"courseId"是同一个,或者说必须一致。而方法public String viewCourse的方法参数Integer courseId也未必必定要与其同名。apache
另外,方法的两个参数Integer courseId和Model model分别负责请求参数的接收 和 结果属性的返回。Model model能够接收咱们须要返回或者说须要在view的JSP页面显示中要用到的某个属性对象。例如,这里的model.addAttribute(course);里的course必须与下面\WEB-INF\jsps\course_overview.jsp中的代码段中course同名,这是这种model.addAttribute默认的。固然将结果返回也有三种方式,即model、Map、ModelAndView。这个之前的博客中说过,不过springMVC会在内部自动将它们都转换成ModelAndView。api
Map返回方式我在下面一个rest风格再做说明。服务器
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>HappyBKs喜欢原先的osc博客页面</title> <link rel="stylesheet" href="<%=request.getContextPath()%>/resources/css/main.css" type="text/css" /> </head> <body> <div id="main"> <div class="newcontainer" id="course_intro"> <div class="course-title">${course.title}</div> <div class="course_info"> <div class="course-embed l"> <div id="js-course-img" class="img-wrap"> <img width="600" height="340" alt="" src="<%=request.getContextPath()%>/${course.imgPath}" class="course_video" /> </div> <div id="js-video-wrap" class="video" style="display: none"> <div class="video_box" id="js-video"></div> </div> </div> <div class="course_state"> <ul> <li><span>学习人数</span> <em>${course.learningNum }</em></li> <li class="course_hour"><span>课程时长</span> <em class="ft-adjust"><span>${course.duration }</span>秒</em></li> <li><span>课程难度</span> <em>${course.levelDesc }</em></li> </ul> </div> </div> <div class="course_list"> <div class="outline"> <h3 class="chapter_introduces">课程介绍</h3> <div class="course_shortdecription">${course.descr}</div> <h3 class="chapter_catalog">课程提纲</h3> <ul id="couList"> <c:forEach items="${course.chapterList}" var="chapter"> <li class="clearfix open"><a href="#"> <div class="openicon"></div> <div class="outline_list l"> <!-- <em class="outline_zt"></em> --> <h5 class="outline_name">${chapter.title }</h5> <p class="outline_descr">${chapter.descr }</p> </div> </a></li> </c:forEach> </ul> </div> </div> </div> </div> </body> </html>
控制台输出:cookie
Rest风格:
最新的处理请求的方式应该算是Rest风格了。
什么是Rest风格?
REST ( REpresentational State Transfer ),State Transfer 为 "状态传输" 或 "状态转移 ",Representational 中文有人翻译为"表征"、"具象",合起来就是 "表征状态传输" 或 "具象状态传输" 或 "表述性状态转移",不过,通常文章或技术文件都比较不会使用翻译后的中文来撰写,而是直接引用 REST 或 RESTful 来表明,由于 REST 一整个观念,想要只用六个中文字来完整表达真有难度。
REST的主要原则有:
用URL表示资源。资源就像商业实体同样,是咱们但愿做为API实体呈现的一部分。一般是一个名词,每一个资源都用一个独一无二的URL来表示。
HTTP方法表示操做。REST充分利用了HTTP的方法,特别是GET、POST、PUT和DELETE。注意XMLHttpRequest对象实现了所有的方法,具体能够参看W3C HTTP 1.1 Specification。
也就是说,客户端的任何请求都包含一个URL和一个HTTP方法。回到上面的例子中,比赛显然是一个实体,那么对于一个特定比赛的请求就表示为:
http://example.com/matches/995
这种方式是清晰明了的,也许和精确命名的方式有所区别,可是只要遵循这种形式,咱们就能很快的进行GET、DELETE、UPDATE和新建操做。
RESTful的原则:
URL表示资源
HTTP方法表示操做
GET只是用来请求操做,GET操做永远都不该该修改服务器的状态。可是这个也要具体状况进行分析,例如一个页面中的计数器,每次访问的时候确实引发了服务器数据的改变,可是在商业上来讲,这并非一个很重要的改变,因此仍然能够接收使用GET的方式来修改数据。
服务应该是无状态的
在有状态的会话中,服务器能够记录以前的信息。而RESTful风格中是不该该让服务器记录状态的,只有这样服务器才具有可扩展性。固然,咱们能够在客户端使用cookie,并且只能用在客户端向服务器发送请求的时候。
服务应当是“幂等”的
“幂等”表示能够发送消息给服务,而后能够再次绝不费力的发送一样的消息给服务。例如,发送一个“删除第995场比赛”的消息,能够发送一次,也能够连续发送十次,最后的结果都会保持一致。固然,RESTful的GET请求一般是幂等的,由于基本上不会改变服务器的状态。注意:POST请求不能被定义为“幂等”,特别是在建立新资源的时候,一次请求建立一个资源,屡次请求会建立多个资源。
拥抱超连接
服务应当自我说明
例如 http://example.com/match/995 请求了一个具体的比赛,可是 http://example.com/match 并无对任何实体进行请求,所以,应当返回一些介绍信息。
服务约束数据格式。数据必须符合要求的格式
好好好,有个概念便可,仍是用例子说话。刚才的需求咱们用rest风格实现,请求应该变成这个样子:http://localhost:8080/mvc/courses/view2/345
固然这里由于是查询功能,因此请求的方法类型是Get,固然,默认也是Get。
因而,咱们的控制器方法能够写为:
//本方法将处理 /courses/view2/123 形式的URL @RequestMapping("/view2/{courseId}") public String viewCourse2(@PathVariable("courseId") Integer courseId, Map<String, Object> model) { log.info("In viewCourse2, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); model.put("course",course); return "course_overview"; }
这里,咱们在仍然须要为rest风格中的请求实体与控制器方法中的方法参数创建映射对应关系。这里,因为rest风格在url中并无提供实体参数的名称,只是在url的某个子串内提供,因此须要对该子串的位置进行标明,即用花括号{}在@ResquestMapping的value中进行标注,并取名。如,本例中的@RequestMapping("/view2/{courseId}")。在控制器方法中,对映射的方法参数须要用另外一个注解@PathVariable来注解相应的方法参数,@PathVariable注解value为@RequestMapping的value中的花括号内的名称,即@RequestMapping("/view2/{courseId}")的{courseId}与@PathVariable("courseId")中的"courseId"是一致的,而控制器方法参数自己的名称无所谓。
控制输出以下:
传统的HttpServlet风格
这种方式其实仍是处理的是带参数的url请求的方法,只不过控制器方法是针对底层servlet的处理方式,这种方式是为了体现通常的servlet编写方式能够与springMVC框架相兼容。
请求http://localhost:8080/mvc/courses/view3?courseId=678
控制器的方法参数用的是HttpServletRequest request,固然,取参数什么的用的仍是传统的HttpServletRequest的请求实体对象的参数取出方法。实体对象的返回用的也是request.setAttribute("course",course);的处理方式。
//本方法将处理 /courses/view3?courseId=123 形式的URL @RequestMapping("/view3") public String viewCourse3(HttpServletRequest request) { Integer courseId = Integer.valueOf(request.getParameter("courseId")); log.info("In viewCourse3, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); request.setAttribute("course",course); return "course_overview"; }
这里值得注意的是,返回结果实体的方式不是利用Model、Map或ModelAndView,而是用最老土的方式,request.setAttribute方法来将属性结果按照键值对的形式给出,键的名称是JSP中的实体名称,值则是控制器方法中处理的实体对象。
这里还须要注意的是,HttpServletRequest并非标准Java SDK中的类,所以需要为项目添加依赖jar包:
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency>
引入:
import javax.servlet.http.HttpServletRequest;
控制台输出:
以上就是控制器处理url请求参数数据的三种方法,下面咱们模拟一个示例,添加一个课程,而后重定向到显示页面。
这里,仅仅是个示例,我不想再添加有关数据库的操做,因此显示部分我仍是用
@RequestMapping("/view2/{courseId}")
public String viewCourse2(@PathVariable("courseId") Integer courseId,
Map<String, Object> model)
来代替,因此重定向的数据会显示为定死的数据,真实状况下,应该是Service部分实现了从数据库中查找到相应课程id的课程实体对象,而后View接受了这个course对象。
首先,添加\WEB-INF\jsps\course_admin\edit.jsp
JSP代码以下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>HappyBKs课程录入页面</title> <link rel="stylesheet" href="<%=request.getContextPath()%>/resources/css/main.css" type="text/css" /> </head> <body> <div id="main"> <div class="newcontainer" id="course_intro"> <form name="mainForm" action="<%= request.getContextPath()%>/courses/save" method="post"> <div> <span>课程名称:</span><input type="text" id="title" name="title"> </div> <div> <span>课程时长:</span><input type="text" id="duration" name="duration"> 秒 </div> <div> <span>课程难度:</span> <select id="level" name="level"> <option value="0">初级</option> <option value="1" selected="selected">中级</option> <option value="2">高级</option> </select> </div> <div> <span>课程介绍:</span> <textarea id="descr" name="descr" rows="5" style="width:480px"></textarea> </div> <div> <input type="submit" id="btnPass" value="提交" /> </div> </form> </div> </div> </body> </html>
插一句吧,request.getContextPath()应该是获得项目的名字(若是项目为根目录,则获得一个"",即空的字条串)。
而后,咱们在控制器中继续添加一个方法:
@RequestMapping(value="/admin", method = RequestMethod.GET, params = "add") public String createCourse(){ return "course_admin/edit"; }
运行时请求 http://localhost:8080/mvc/courses/admin?add 的显示效果以下:
固然咱们还得添加一个表单提交以后相应的控制器方法,即表单中action="<%= request.getContextPath()%>/courses/save"所指示的请求url。
控制器中添加方法以下:
@RequestMapping(value="/save", method = RequestMethod.POST) public String doSave(@ModelAttribute Course course){ log.info("Info of Course:"); log.info(ReflectionToStringBuilder.toString(course)); //在此进行业务操做,好比数据库持久化 course.setCourseId(123); return "redirect:view2/"+course.getCourseId(); }
这里值得注意的有三点,一个是,标记解释某个model实体,能够在控制器方法对应的实体参数上注解@ModelAttribute,相应的实体会被最终传递到对应的view资源。
第二,重定向到某个页面,只须要在返回的结果字符串前加上“redirect:”便可。
第三,ReflectionToStringBuilder.toString是org.apache.commons.lang.builder.ReflectionToStringBuilder中的方法,固然,添加相应的POM坐标也是必然的。详细能够参见往前本系列特别篇开始之后的博客文章。
像以前说的,因为没有修改Service中的实现,数据是模拟的,这里就不把展现页面截图了。
这里只把控制台输出:
表单提交后,接收请求的控制器方法doSave输出的实体对象键值对(ReflectionToStringBuilder真好用啊)。
重定向以后的输出日志。
好,最好我把本篇的控制器类完整给出:
package com.happyBKs.controller; import com.happyBKs.model.Course; import com.happyBKs.service.CourseService; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * Created by happyBKs on 2016/6/15. */ @Controller @RequestMapping("/courses") // /courses/** public class CourseController { //完成日志信息 private static Logger log= LoggerFactory.getLogger(CourseController.class); private CourseService courseService; //使用spring容器管理里了对应的依赖关系 @Autowired public void setCourseService(CourseService courseService) { this.courseService = courseService; } //提供完成一个业务的方法:根据课程ID查询课程内容。 //本方法将处理 /courses/view?courseId=123 形式的URL @RequestMapping(value="/view", method= RequestMethod.GET) public String viewCourse(@RequestParam("courseId") Integer courseId, Model model) { //日志输出,查看请求的courseId是否是咱们的courseId log.info("In viewCourse, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); model.addAttribute(course); return "course_overview"; } //本方法将处理 /courses/view2/123 形式的URL @RequestMapping("/view2/{courseId}") public String viewCourse2(@PathVariable("courseId") Integer courseId, Map<String, Object> model) { log.info("In viewCourse2, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); model.put("course",course); return "course_overview"; } //本方法将处理 /courses/view3?courseId=123 形式的URL @RequestMapping("/view3") public String viewCourse3(HttpServletRequest request) { Integer courseId = Integer.valueOf(request.getParameter("courseId")); log.info("In viewCourse3, courseId = {}", courseId); Course course = courseService.getCoursebyId(courseId); request.setAttribute("course",course); return "course_overview"; } @RequestMapping(value="/admin", method = RequestMethod.GET, params = "add") public String createCourse(){ return "course_admin/edit"; } @RequestMapping(value="/save", method = RequestMethod.POST) public String doSave(@ModelAttribute Course course){ log.info("Info of Course:"); log.info(ReflectionToStringBuilder.toString(course)); //在此进行业务操做,好比数据库持久化 course.setCourseId(123); return "redirect:view2/"+course.getCourseId(); } }