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();
    }


}
相關文章
相關標籤/搜索