學習SpringMVC必知必會(3)~springmvc的請求和響應

一樂樂發表於2022-03-24

一、處理器方法響應處理

Controller方法該怎麼返回、Controller資料該怎麼進行共享

  • 返回void/ModelAndView/String

1、Controller方法返回void

	//返回void型別,此時可以把Controller方法當做Servlet使用【適合用來下載檔案】
	@RequestMapping("/test1")
	public void test(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//請求轉發
		request.getRequestDispatcher("/WEB-INF/views/welcome.jsp").forward(request, response);
		//設定共享資料
		request.setAttribute("msg", "hello");
		//輸出json格式
		response.setContentType("text/json;charset=utf-8");
		response.getWriter().println();	
	}

2、【常用】返回ModelAndView

	//返回ModelAndView
	@RequestMapping("/test3")
	public ModelAndView test3() {
		ModelAndView mv = new ModelAndView();
		//設定檢視名稱
//		mv.setViewName("/WEB-INF/views/welcome.jsp");
		mv.setViewName("welcome");
		mv.addObject("msg", "返回ModelNAndView");//設定共享資料的key和value
		mv.addObject("叩丁狼教育");//設定共享資料的value,此時會把value型別的首字母作為key:string
		return mv;
	}

3、【常用】返回String型別(是邏輯檢視名稱),引數是Model型別(是共享資料)

	//返回string,邏輯檢視名稱,此時需要結合引數Model型別 一起使用
	@RequestMapping("/test4")
	public String test4(Model model) {
		//設定共享資料
		model.addAttribute("叩丁狼");
		model.addAttribute("msg", "教育");
		return "welcome";//設定跳轉的檢視
	}




二、請求跳轉

  • 請求轉發、URL重定向、URL重定向共享資料

1、請求轉發

	//請求轉發,瀏覽器位址列不變,可以共享請求中的資料
	//原理:request.getRequestDispatcher("").forward(request, response);
	@RequestMapping("/test5")
	public String test5(Model model) {
		return "forward:/hello.jsp";//設定跳轉的檢視
	}

2、URL重定向

	//重定向,瀏覽器位址列改變,不能共享請求中的資料
	//原理:request.sendRedirect("");
	@RequestMapping("/test6")
	public String test6(Model model) {
		return "redirect:/hello.jsp";//設定跳轉的檢視
	}

■ 請求資源的路徑問題:[總結:訪問資源的時候,都使用/開頭]

  • 訪問資源的時候前面帶上/,表示絕對路徑,根路徑開始去尋找資源
  • 訪問資源的時候簽名不加/,表示相對路徑,上一級上下文路徑中去尋找資源

✿ 請求轉發和URL重定向的選擇:

請求轉發/URL重定向 請求轉發 URL重定向
位址列改變? 不會 會改變
共享資料? 可以 不可以?
表單重複提交? 會發生 不會發生
  • 傳統的方式,在url重定向的時候,因為是兩次不同的請求,所以不能共享請求中的資料。

    在開發中,有時候真的需要重定向跳轉後共享資料------------spring3.1開始,提供了Flash屬性。
    只能是從Controller 重定向到 Controller,不能到jsp



3、URL重定向共享資料

	//重定向:從a跳轉到b	
	@RequestMapping("/a")
	public String a(RedirectAttributes ra) {
		ra.addAttribute("msg1", "a傳遞的資料");
		ra.addFlashAttribute("msg2", "msg2");
		return "redirect:/response/b";//設定跳轉的檢視
	}
	
	@RequestMapping("/b")
	public ModelAndView b(String msg1, @ModelAttribute("msg2") String msg2) {
		System.out.println("msg1:" + msg1);
		System.out.println("msg2:" + msg2);
		return null;
	}

  • 重定向共享資料的原理:更大的作用域--session

image




三、處理器方法引數處理(接收請求引數的處理

  • 處理器方法的請求引數該怎麼攜帶、請求引數該怎麼獲取

1、request 和 response 引數

■ 情況一:為了操作Servlet API 物件,此時可以直接以引數形式傳遞,也可以直接使用DI注入。

@Controller
@RequestMapping("/request")
public class HandlerRequestController {

	@Autowired
	private ServletContext context;
	
	@RequestMapping("/test1")
	public void test(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws ServletException, IOException {
		System.out.println("request:" + request);
		System.out.println("response:" + response);
		System.out.println("session:" + session);
		System.out.println("ServletContext:" + this.context);
		
	}
}
  • request、response、session:建議使用引數
  • context 上下文:單例,建議使用屬性,注入

因為Controller(Servlet) 是單例的,執行緒不安全,一般不用成員變數,除非要共享的資料,才作為成員變數。



2、簡單型別引數

  • 處理簡單型別的請求引數

■ 獲取請求引數:【保證輸入的引數和定義的形參名稱一致】

	//獲取請求引數:通過保證請求引數名稱和Controller方法定義的形參(入參)同名即可
	@RequestMapping("/test2")
	public void test2(String username, int age)  {
		System.out.println(username);
		System.out.println(age);
	}

image


■ 獲取請求引數:【輸入的引數和定義的形參名稱不一致】----註解@RequestParam

	//獲取請求引數:若請求引數和請求引數名稱和形參不同----註解@RequestParam
	@RequestMapping("/test3")
	public void test3(@RequestParam("name") String username, @RequestParam(value="age",required=false) Integer age)  {
		System.out.println("username:" +username);
		System.out.println("age:" + age);
	}

3、中文亂碼處理

■ 在"全域性配置"【web.xml】,新增上編碼過濾器:

	<!-- (針對post請求)配置過濾器 -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<!-- 配置初始化引數 -->
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceRequestEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>forceResponseEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

4、restfull風格傳參

  • restfull 風格:軟體架構風格,嚴格講是一種編碼風格,充分利用http協議本身的語義,從而提供一些設計原則和約束條件,主要是用來作為客戶端和服務端的互動。
  • 簡潔、有層次、容易實現快取機制
	//restfull風格傳遞引數
	//PathVariable: 可以將url中佔位符繫結到方法的形參中
	@RequestMapping("/r/{id}")
	public ModelAndView rest(@PathVariable("id") Integer id) {
		System.out.println("id:" + id);
		return null;
	}

5、陣列和List型別引數

	//接收一個引數有很多值的情況
	//使用陣列:可以直接接收  Long[] ids
	//使用List: 不可以直接接收,可以在物件中存在一個集合   List<Long> ids
	@RequestMapping("/batchDelete") //batchDelete?ids=10&ids=20&ids=30
	public void batchDelete(FormBean fb) {
		System.out.println(fb.getIds());	
	}
★ 操作一個引數有多個值的情況,一般直接使用陣列接收即可,或者使用javaBean物件來封裝資料。

6、■ 獲取請求引數:

JavaBean型別引數【將請求引數封裝成一個物件

	//封裝成一個物件
	@RequestMapping("/bean") //batchDelete?ids=10&ids=20&ids=30
	public void bean(User user) {
		System.out.println(user);	
	}

  • 請求引數封裝成javaBean物件,瀏覽器位址列直接輸入引數為物件的屬性即可.

image



✿ 總結處理器方法引數處理:


1、直接把請求引數封裝成javaBean物件
2、配置解決中文亂碼的過濾器
3、簡單型別引數----輸入引數名和定義的形參不同-> @ResultParam
4、使用陣列接收多個引數
5、restfull風格傳參




四、ModelAttribute

  • 給共享的model資料設定key名,貼在形參上,也可以貼在方法上,

    針對複合型別引數,預設情況下就會放到model中(共享), 預設的key就是型別首字母小寫

    • 使用註解 @ModelAttribute,起個別名
	//ModelAttribute註解:
	/*
	 *1、給共享的model資料設定key名,貼在形參上,也可以貼在方法上
	 *	針對複合型別(非簡單型別)引數,預設情況下就會放到model中(共享), 預設的key就是型別首字母小寫
	 * 2、可以標註一個非請求處理的方法,被標註的方法,每次在請求處理方法之前都會優先被執行[存放多個請求需要共享的資料]
	 */
	@RequestMapping("/test11")
	public String test1(@ModelAttribute("u") User user)  {
		System.out.println(user);
		return "welcome";
	}




五、其他請求資訊

1、獲取請求頭 @RequestHeader

	@RequestMapping("/test1")
	public ModelAndView test1(@RequestHeader("User-Agent") String userAgent) {
		System.out.println("test1");
		System.out.println("User-Agent:" + userAgent);
		return null;
	}

2、獲取Cookie @CookieValue

	@RequestMapping("/test1")
	public ModelAndView test1(@RequestHeader("User-Agent") String userAgent, @CookieValue("Webstorm-5895a979") String cName) {
		System.out.println("test1");
		System.out.println("User-Agent:" + userAgent);
		System.out.println("cName:" + cName);
		return null;
	}

3、操作HttpSession @SessionAttributes

  • 預設情況下模型資料是儲存到 request 作用域的
@Controller
@RequestMapping("/other")
@SessionAttributes("errMsg")
public class OtherController {

	@RequestMapping("/test2")
	public String test2(Model model) {
		System.out.println("操作session");
		model.addAttribute("errMsg", "錯誤資訊");
		return "redirect:/hello.jsp";
	}
}




六、資料繫結流程

image

1、框架把 ServletRequest 物件和請求引數傳遞給 DataBinder

2、DataBinder 首先呼叫 Spring Web 環境中的 ConversionService 元件,進行資料型別轉換和格式化等操作,將 ServletRequest 中的資訊填充到形參物件中;

3、DataBinder 然後呼叫 Validator 元件對已經繫結了請求訊息資料的形參物件進行資料合法性校驗

4、DataBinder 最後輸出資料繫結結果物件 BindingResult

  • BindingResult 包含了已完成資料繫結的形參物件和校驗錯誤資訊物件




七、多物件封裝傳參

	/*
	 * 需要吧表單資料封裝到多個物件中去,若各個物件有相同的屬性時
	 * 不知道該把哪一個引數封裝到哪一個物件
	 */
	@RequestMapping("/save")
	public ModelAndView save(Cat cat, Dog dog) {
		System.out.println("提交資料");
		System.out.println(cat);
		System.out.println(dog);
		return null;
	}
  • input.jsp 傳遞多個物件的引數時:
	<form action="/save" method="post">
		貓名:<input type="text" name="name" /><br/>
		貓年齡:<input type="text" name="age" /><br/>
		狗名:<input type="text" name="name" /><br/>
		狗年齡:<input type="text" name="age" /><br/>
		<input type="submit" value="提交">
	</form>

image


解決:

  • input.jsp:加上字首做區分
	<form action="/save" method="post">
		貓名:<input type="text" name="cat.name" /><br/>
		貓年齡:<input type="text" name="cat.age" /><br/>
		狗名:<input type="text" name="dog.name" /><br/>
		狗年齡:<input type="text" name="dog.age" /><br/>
		<input type="submit" value="提交">
	</form>
  • 處理器Controller新增方法:
	//從引數--> 物件,封裝規則需要我們來設定
	
	//自定義資料繫結註冊,將請求引數轉化成對應物件的屬性
	@InitBinder("cat")
	public void initBinderCat(WebDataBinder binder) {
		//設定欄位以什麼做為字首
		binder.setFieldDefaultPrefix("cat.");
	}
	
	@InitBinder("dog")
	public void initBinderDog(WebDataBinder binder) {
		//設定欄位以什麼做為字首
		binder.setFieldDefaultPrefix("dog.");
	}

image




八、JSON資料處理

  • JSON處理

1、依賴:

  • jackson-annotations-2.12.2.jar
  • jackson-core-2.12.2.jar
  • jackson-databind-2.12.2.jar

2、處理json的註解 @ResponseBody @RestController @RequestBody

(1) @ResponseBody: 處理響應,把物件轉化成json字串

  • @ResponseBody 處理響應,把物件轉化成json字串
    • 貼到方法上:只會針對當前方法做json處理
    • 貼到類上:會對當前類中所有方法做json處理
	//把單個物件/Map轉化成json格式
	@RequestMapping("/test1")
	@ResponseBody
	public User test1() {
		User u = new User();
		u.setUsername("shan");
		u.setAge(18);
		return u;
	}
	
	//把多個物件轉化成json格式
	@RequestMapping("/test2")
	@ResponseBody
	public List<User> test2() {
		User u = new User();
		u.setUsername("shan");
		u.setAge(18);
		return Arrays.asList(u, u, u);
	}


	//返回一個String,預設返回字串是邏輯檢視名稱,加上@ResponseBody,當做json格式的資料
	@RequestMapping(value="/test3", produces="application/json;charset=utf-8")
	@ResponseBody
	public String test3() {
		return "success, 你好~";
	}

(2) @RestController = @Controller + @ResponseBody


(3) @RequestBody

  • @RequestBody: 處理請求,用於讀取Http請求的內容,把json格式的請求資料封裝成物件
    • application/x-www-form-urlencoded: 表單提交用得比較多,是傳統的key-value格式,處理起來非常方便,無需RequestBody都可以,貼上也可以
    • application/multipart: 檔案上傳的請求,springmvc 裝飾設計模式,既可以處理檔案上傳,也可以處理表單資料
    • application/json: 引數是json格式的,此時必須使用RequestBody\
    • application/xml




九、日期型別處理

1、前臺往後臺傳參轉化為Date型別

★ 時間格式的註解:@DateTimeFormat

  • 注意細節:時間Date在util包(java中常用)有,在sql包也有,使用快捷鍵導包的時候可能會預設自動報錯包~
	//從前臺---->後臺傳遞引數 java.lang.String -> java.util.Date
	//請求引數是Date型別
	@RequestMapping("/test1")
	public ModelAndView test(@DateTimeFormat(pattern = "yyyy-MM-dd")Date date) {
		System.out.println("date:" + date);
		if(date instanceof Date) {
			System.out.println("yes");
		}
		return null;
	}

  • 物件的屬性有時間Date型別的,方式一:貼註解 @DateTimeFormat

    ◆ 方式二:資料繫結的時候處理,通過@InitBinder定義處理時間格式的方法

	@InitBinder
	public void initBinder(WebDataBinder binder) {
		//日期格式
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		//屬性編輯器
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
	}
  • 讓專案中的所有用到時間格式的類都使用到我們定義的處理時間格式的方法,我們將方法抽離出去,定義成一個類,貼上註解處理增強 @ControllerAdvice

    只要抽離的類在ioc的註解驅動掃描範圍內,即可~

@ControllerAdvice
public class DateFormateAdvice {

	@InitBinder
	public void initBinder(WebDataBinder binder) {
		//日期格式
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		//屬性編輯器
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
	}
}



2、jsp中顯示時間格式:將歐美時間格式-->中國時間格式

(1)匯入jar包[tomcat伺服器examples案例下的lib就有]:

  • taglibs-standard-impl-1.2.5.jar
  • taglibs-standard-spec-1.2.5.jar

(2)配置引入標籤庫taglib

<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 

(3) 使用jstl:

	<% pageContext.setAttribute("myDate", new java.util.Date()); %>	
	北京時間: <fmt:formatDate value="${myDate}" pattern="yyyy=MM-dd HH:mm:ss"/>

image



3、後臺往前臺響應JSON時---Date型別

(1)方法1:在springmvc框架配置中新增json型別轉化相關的配置[配置全域性解析器]:

	<!-- MVC註解解析器 -->
	<mvc:annotation-driven>
		<mvc:message-converters>
			<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
				<property name="objectMapper">
					<bean class="com.fasterxml.jackson.databind.ObjectMapper">
						<property name="dateFormat">
							<bean class="java.text.SimpleDateFormat">
								<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
							</bean>
						</property>
					</bean>
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>

(2)方式2:使用註解@JsonFormat

@Data
public class User {
	private Long id;
	private String username;
	private Integer age;
	//@DateTimeFormat(pattern = "yyyy-MM-dd")
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	private Date hireDate;	
}

★ 注意:@DateTimeFormat前臺往後臺傳參【前臺String型別-》後臺Date型別】

★ 注意: 是後臺響應給前臺,響應為json格式(對時間Date型別的處理)



如果本文對你有幫助的話記得給一樂點個贊哦,感謝!

相關文章