springMVC笔记系列(20)——控制器实现详解(下)

此次接着上次的博客继续将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();
    }


}
相关文章
相关标签/搜索