@RestController // @Controller+/@ResponseBody
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public String str() {
return "hello @ResponseBody...";
}
}
SpringMVC常用注解
springmvc.xml常用配置
扫包、静态资源放行、开启注解驱动
<!--扫描包-->
<context:component-scan base-package="cn.xxx"/>
<!--静态资源放行-->
<mvc:default-servlet-handler/>
<!--开启注解驱动-->
<mvc:annotation-driven />
@PathVariable
用于接收Rest风格请求的路径参数;将方法形参和url的占位符进行绑定
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
-
name/value:当url中的占位符和方法形参名称不一致时,可以使用name/value属性进行绑定
-
required:是否必须提供占位符,默认为true
测试:
@Controller
@RequestMapping("/demo01")
public class Demo01Controller_PathVariable {
@GetMapping("/{id}/{name}")
public void test(
@PathVariable Integer id,
@PathVariable(value = "name") String cityName, // 把url上的name值赋值给cityName
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: "+id+"<hr />");
response.getWriter().write("cityName: "+cityName+"<hr />");
}
@GetMapping("/{id}")
public void test2(
@PathVariable Integer id,
@PathVariable(value = "aaa",required = false) String cityName, // 没有aaa占位符也没关系
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: "+id+"<hr />");
response.getWriter().write("cityName: "+cityName+"<hr />");
}
}
@RequestParam
接收前端提交的参数(POST/GET提价都可以接收);当接收的参数的变量名与表单的name属性不一样时可以使用@RequestParam来绑定,同时可以封装其他类型的对象,如List、Set、Map
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
-
name/value:当前端传递的参数名称和方法形参名称不一致时,可以使用name/value属性进行绑定;
-
required:是否必须提供传递此参数,默认为true;
-
defaultValue:如果前端没有传递此参数,默认为defaultValue的值;
测试:
@Controller
@RequestMapping("/demo02")
public class Demo02Controller_RequestParam {
@GetMapping("/demo01")
public void demo01(Integer id,
@RequestParam("name") String cityName, // 将前端传递的name参数与cityName进行绑定(name参数必须传递)
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: " + id + "<hr />");
response.getWriter().write("cityName: " + cityName + "<hr />");
}
@GetMapping("/demo02")
public void demo02(@RequestParam(required = false) Integer id, // id可以不传递
@RequestParam(defaultValue = "guangzhou") String cityName, // cityName参数如果没有传递默认为guangzhou
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: " + id + "<hr />");
response.getWriter().write("cityName: " + cityName + "<hr />");
}
/**
* map类型
* @param maps
* @param response
* @throws IOException
*/
@GetMapping("/demo03")
public void demo03(@RequestParam Map<String, Object> maps,
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: " + maps.get("id") + "<hr />");
response.getWriter().write("cityName: " + maps.get("cityName") + "<hr />");
}
/**
* List类型
* @param ids
* @param response
* @throws IOException
*/
@PostMapping("/demo04")
public void demo04(@RequestParam("ids") List<Integer> ids,
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("id: " + ids.toString() + "<hr />");
}
}
@CookieValue
获取Cookie中的数据
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
-
name/value:Cookie中的参数名称和方法形参名称不一致时,可以使用name/value属性进行绑定;
-
required:是否必须提供此参数Cookie参数,默认为true;
-
defaultValue:如果Cookie中没有此参数,默认为defaultValue的值;
测试:
@Controller
@RequestMapping("/demo03")
public class Demo03Controller_CookieValue {
@GetMapping("/demo01")
public void demo01(@CookieValue String JSESSIONID, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("JSESSIONID: " + JSESSIONID + "<hr />");
}
@GetMapping("/demo02")
public void demo02(HttpServletResponse response) throws IOException {
Cookie cookie = new Cookie("cityName", "guangzhou");
response.addCookie(cookie);
response.setContentType("text/html;charset=utf8");
response.getWriter().write("ok");
}
@GetMapping("/demo03")
public void demo03(@CookieValue String cityName, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("cityName: " + cityName + "<hr />");
response.getWriter().write("ok");
}
}
@RequestHeader
获取请求头中的数据
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
测试:
@Controller
@RequestMapping("/demo04")
public class Demo04Controller_RequestHeader {
@GetMapping("/demo01")
public void demo01(@RequestHeader String Host,
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("Host: " + Host + "<hr />");
}
@GetMapping("/demo02")
public void demo02(@RequestHeader String username,
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("username: " + username + "<hr />");
}
}
@SessionAttribute
从session中获取一个值封装到参数中
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SessionAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
测试:
@Controller
@RequestMapping("/demo05")
public class Demo05Controller_SessionAttribute {
@GetMapping("/demo01")
public String demo01(HttpSession session) throws IOException {
session.setAttribute("loginUser", "zhangsan");
return "redirect:/demo05/demo02";
}
/**
* 从session中取出loginUser(必须要有这个属性)
* @param loginUser
* @param response
* @throws IOException
*/
@GetMapping("/demo02")
public void demo02(@SessionAttribute String loginUser,
HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("loginUser: " + loginUser + "<hr />");
}
}
@SessionAttributes
把BindingAwareModelMap中的指定的key或者指定的属性的值也存入一份进session域
tips:@SessionAttributes注解并不能把存入request域的值也存入session中
源码:
@Target({ElementType.TYPE}) // 标注在类上的
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
@AliasFor("names")
String[] value() default {};
@AliasFor("value")
String[] names() default {};
Class<?>[] types() default {};
}
测试:
@SessionAttributes(value = {"username"}, types = {Integer.class})
@Controller
@RequestMapping("/demo06")
public class Demo06Controller_SessionAttributes {
@GetMapping("/demo01")
public String demo01(Map<String, Object> map, HttpServletRequest request) throws IOException {
map.put("username", "admin");
map.put("password", "123");
map.put("age", 23);
map.put("tall", 1.78);
// 存入request域的值不能被存入session
request.setAttribute("test",111);
return "redirect:/demo06/demo02";
}
@GetMapping("/demo02")
public void demo02(HttpSession session, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf8");
response.getWriter().write("username: " + session.getAttribute("username") + "<hr />");
response.getWriter().write("password: " + session.getAttribute("password") + "<hr />");
response.getWriter().write("age: " + session.getAttribute("age") + "<hr />");
response.getWriter().write("tall: " + session.getAttribute("tall") + "<hr />");
response.getWriter().write("test: " + session.getAttribute("test") + "<hr />");
}
}
@RequestAttribute
获取request域中的数据;
测试:
@Controller
@RequestMapping("/demo07")
public class Demo07Controller_RequestAttribute {
@GetMapping("/demo01")
public String demo01(Model model) throws IOException {
// 在请求域中添加几个属性
model.addAttribute("username", "zhangsan");
model.addAttribute("password", "123");
return "/demo07/demo02";
}
@GetMapping("/demo02")
public void demo02(
@RequestAttribute("username") String name, // 获取请求域中的username属性值
@RequestAttribute String password, // 获取请求域中password属性值
HttpServletResponse response
) throws IOException {
response.getWriter().println("username: "+name);
response.getWriter().println("password: "+password);
}
}
@ModelAttribute
@ModelAttribute注解可以作用于方法和参数上
-
作用于方法上:在执行Controller其他所有方法之前执行@ModelAttribute注解标注的方法
-
作用于参数上:获取ModelAttribute标注的方法中BingMap中的某个值到某个属性到形参上,并且此次的BingMap会传递到下一个handler方法;BingMap中的值以前端传递的为准,如果前端传递了参数则以前端为准,如果没有传递参数则保持@ModelAttribute标注方法中的默认值;
源码:
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean binding() default true;
}
测试:
@Controller
@RequestMapping("/demo08")
public class Demo08Controller_ModelAttribute {
@ModelAttribute
public void model(Model model, ModelMap modelMap, Map map) {
model.addAttribute("username", "zhangsan");
modelMap.addAttribute("password", "admin");
map.put("email","zhangsan@qq.com");
}
@RequestMapping("/demo01")
public void demo01(
@ModelAttribute("username") String username, // 注意: 形参名和BindingMap中的key一样也要写
@ModelAttribute("password") String password,
@ModelAttribute("email") String email,
HttpServletResponse response
) throws Exception {
response.getWriter().println("username: " + username);
response.getWriter().println("password: " + password);
response.getWriter().println("email: " + email);
}
}
Tips:@ModelAttribute只能获取BingMap中的数据,不能获取request中的数据;
应用场景:现在需要修改一个City信息,有某些应用信息前端不提交(默认为null),
表单:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/demo08/demo01">
城市ID:<input type="text" name="id">
城市名称:<input type="text" name="cityName">
GDP:<input type="text" name="GDP">
是否省会:<input type="text" name="capital">
<input type="submit">
</form>
</body>
</html>
Controller
@Controller
@RequestMapping("/demo09")
public class Demo09Controller_ModelAttribute02 {
/**
* ModelAttribute标注的方法在所有的handler方法之前执行
*
* @param maps
* @throws IOException
*/
@ModelAttribute
public void model(Map<String, Object> maps) throws IOException {
maps.put("city", new City(1, "南宁", 4700.00, true));
System.out.println("model方法执行了...");
}
// 如果前端没有传递则以@ModelAttribute标注的为准,如果传递了则以前端参数为准
@RequestMapping("/demo01")
public void demo01(@ModelAttribute("city") City city, HttpServletResponse response) throws Exception{
response.setContentType("text/html;charset=utf8");
response.getWriter().println(city);
}
}
SpringMVC的放行
SpringMVC提供的拦截规则
-
.form:代表以.form结尾的后缀请求都会进入springmvc管
-
/:代表除了JSP以外拦截所有,html、css、js等静态web资源也会拦截
-
/*:拦截所有请求
缺省Servlet
依赖:
//Servlet 容器支持
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.41</version>
<!--此jar包在tomcat中已经有了,但是如果不引入此依赖项目则会报错,因此依赖范围provided就行-->
<scope>provided</scope>
</dependency>
//MVC架构支持
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.11</version>
</dependency>
Maven的依赖范围
-
compile:编译时存在,测试时存在,运行时存在,会打入war包;(普通jar包)
-
provided:编译时存在,测试时存在,运行时不存在,不会打入war包;(servlet-api)
-
runtime:编译时不存在,测试时存在,运行时存在,会打入war包(mysql驱动)
-
Test:编译时不存在,测试时存在,运行时不存在,不会打入war包(Junit)
-
system:编译时存在,测试时存在,运行时存在,不会打入war包(仅限于本地环境)
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置 SpringMVC 的前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置 SpringMVC 的前端控制器的映射 -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置缺省 Servlet 的映射 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启 SpringMVC 的注解支持 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
/WEB-IINF/views/hello.jsp
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Page</title>
</head>
<body>
<h1>Hello, SpringMVC!</h1>
</body>
</html>
启动服务器,访问http://localhost:8080/hello
可以看到 SpringMVC 处理的页面
启动服务器,访问http://localhost:8080/hello
可以看到 SpringMVC 处理的页面
通过以上步骤,你可以验证 SpringMVC 和缺省 Servlet 的不同映射和处理方式。
缺省servlet放行原理是:如果是配置的后缀请求直接进入缺省servlet,而不是进入SpringMVC进行处理(不会经过SpringVMC);
Tips:配置缺省servlet放行时,SpringMVC的拦截规则不能为/,因为缺省Servlet不能放行JSP请求,如果SpringMVC拦截规则为/,然后缺省Servlet又不能放行,那么访问JSP时,会出现404
resources放行
springmvc.xml
<!--
resources:配置springmvc放行
mapping:映射地址
location:具体目录
/js/*代表js下面的所有资源不拦截
/js/** 代表js下所有的资源不拦截包括子孙包
-->
<mvc:resources mapping="/js/**" location="/js/" />
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/imgs/**" location="/imgs/" />
<mvc:resources />由Spring MVC框架自己按配置的规则处理静态资源,并添加一些有用的附加值功能。
注意:JSP页面不属于静态资源!如果是常见的浏览器能解析的格式,直接按照协议返回,如果不是浏览器能直接解析的会返回下载头导致下载该jsp页面!
修改web.xml的前端处理器
<!--拦截所有请求(包括JSP)-->
<url-pattern>/*</url-pattern>
修改springmvc
<mvc:resources mapping="/jsp/**" location="/jsp/" />
访问:http://localhost:8080/jsp/demo01.jsp
浏览器默认以下载的方式处理本次请求的响应
Handler放行
springmvc.xml
<mvc:default-servlet-handler />
SpringMVC会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler;
对进入DispatcherServlet的URL进行筛查
如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理
如果不是静态资源的请求,才由DispatcherServlet继续处理。
这种方式同样只能放行静态资源,JSP不属于静态资源;
web.xml
<!--拦截所有请求(包括JSP)-->
<url-pattern>/*</url-pattern>
放行规则小结
-
1)缺省Servlet:请求没有进入springmvc,而是直接进入缺省servlet进行处理放行
-
2)resources:实质上请求是进入了springmvc然后由springmvc自己处理资源,达到放行效果
-
3)handler:请求进入springmvc然后对其进行筛选,发现是个"静态"资源就交给缺省servlet去处理,但是缺省servlet只会处理静态资源,如果是jsp会特殊处理(下载、无法解析等)!
JSON支持
SpringMVC支持自动将JSON转换成Java对象,也支持将Java对象自动转成JSON,SpringMVC本身没有对JSON数据处理的类库,要支持JSON的自动转换必须导入JSON的支持包
依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>8.5.41</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
</dependencies>
响应JSON
@ResponseBody
将响应的信息放入响应体中,如果响应的数据是Java对象,则会将本次的响应类型改为application/json
Controller
@Controller
@RequestMapping("/demo01")
public class Demo01Controller {
@ResponseBody // 将结果集以json形式响应
@GetMapping("/demo01")
public City demo01() throws Exception {
City city = new City(1, "南昌", 5500.0, true);
return city;
}
@ResponseBody // 将结果集以json形式响应
@GetMapping(value = "/demo02")
public List<City> demo02() throws Exception {
List<City> cityList = new ArrayList<>();
cityList.add(new City(1, "南京", 14000.00, true));
cityList.add(new City(2, "南通", 2400.00, false));
cityList.add(new City(3, "南阳", 4000.00, false));
return cityList;
}
@ResponseBody
@GetMapping(value = "/demo03")
public String demo03() throws Exception {
return "<h1>hello</h1>"; // 响应的信息被放在响应体中,而不是当做视图跳转
}
}
@ResponseBody注解不仅可以标注在方法上,还可以标注在类上
当该注解标注在类上时,该类的所有方法的返回值都会当作Json响应到前端
相当于在该类的所有的方法上都标注了@ResponseBody注解
@RestController
@RestController是@Controller+@ResponseBody注解的整合版,拥有两个注解的功能
既能当前Bean注入到IOC容器,又能让当前Controller的所有方法的返回值以Json响应
Controller
@RestController // @Controller+/@ResponseBody
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public String str() {
return "hello @ResponseBody...";
}
}
@JsonIgnore
将实体转换为json时指定忽略的属性,使该属性不出现在JSON信息上
@Data
@AllArgsConstructor
@NoArgsConstructor
public class City {
private Integer id; // 城市id
private String cityName; // 城市名称
private Double GDP; // 城市GDP,单位亿元
private Boolean capital; // 是否省会城市
@JsonIgnore
private Province province; // 所属省份
public City(Integer id, String cityName, Double GDP, Boolean capital) {
this.id = id;
this.cityName = cityName;
this.GDP = GDP;
this.capital = capital;
}
}
@JsonFormat
将需要转换成JSON的实体类中的Date按照指定的格式转换为JSON字符串
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
private Date birthday;
}
ResponseEntity
ResponseEntity对象的内容更加丰富,包括响应头、响应体、响应状态码等信息
@ResponseBody
@RequestMapping(value = "/demo06")
public ResponseEntity<City> demo06() {
// 响应体
City city = new City(1, "南宁", 5000.0D, true, new Province(1, "广西"));
// 响应头
HttpHeaders headers = new HttpHeaders();
headers.put("test", Arrays.asList("123"));
// 创建一个响应报文、响应状态码
ResponseEntity<City> responseEntity = new ResponseEntity<>(city, headers, HttpStatus.OK);
return responseEntity;
}
前端接收响应报文:
Status Code:200 OK
test:123
请求JSON
@RequestBody
获取请求体的内容
注意:GET请求方式是没有请求体的;
Controller
@Controller
@RequestMapping("/demo02")
public class Demo02Controller {
@RequestMapping("/demo01")
public void demo01(@RequestBody String str, HttpServletResponse response) throws Exception { // 获取请求体的内容
System.out.println(str);
response.getWriter().write(str);
System.out.println("--------------");
}
}
demo01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="/js/jquery-3.5.1.min.js"></script>
</head>
<body>
<form action="/demo02/demo01" method="post">
<input type="text" name="id">
<input type="text" name="name">
<input type="submit">
</form>
<button id="btn">sendRequest</button>
<script>
$(function () {
$("#btn").click(function () {
// Json对象
var province = {id: 1, name: "吉林省"}
$.post({
url: "/demo02/demo01",
// ajax默认的提交格式为: application/x-www-form-urlencoded
contentType: "application/x-www-form-urlencoded",
data: province,
success: function (res) {
console.log(res);
}
})
})
})
</script>
</body>
</html>
Json的支持
@RequestBody不仅可以接收请求体中的参数
还可以将请求体数据为Json的转换为Java实体类型;但要求本次请求类型必须为application/json
Controller
@ResponseBody
@RequestMapping("/demo02")
public Province demo02(@RequestBody Province province) throws Exception {
System.out.println(province);
return province;
}
按钮
<button id="json">sendJson</button>
JavaScript
$("#json").click(function () {
// Json对象
var province = {id: 1, name: "辽宁省"}
// 一定要转换为Json字符串
var provinceJson = JSON.stringify(province)
$.post({
url: "/demo02/demo02",
// ajax默认的提交格式为: application/x-www-form-urlencoded
contentType: "application/json",
data: provinceJson,
success: function (res) {
console.log(res);
}
})
})
Tips:
-
1)请求类型(Content-Type)必须为application/json,否则出现415(请求类型不匹配)
-
2)请求参数必须为Json字符串,否则出现400(请求参数错误)
注意:JQuery在传递Json对象(不是Json字符串)数据到后台时,在底层会将Json对象转换为form表单进行传递;
如果本次的提交类型(Content-Type)为application/x-www-form-urlencoded,那么在后端也是可以封装为Java对象的,前提是不能使用@RequestBody注解
Controller
@RequestMapping("/demo03")
@ResponseBody
public Province demo03(Province province) throws Exception {
return province;
}
RequestEntity
RequestEntity对象的内容更加丰富,包含请求头、请求体等
Controller
@RequestMapping("/demo04")
@ResponseBody
public String demo04(HttpEntity<String> entity) throws Exception {
// 获取请求头
HttpHeaders headers = entity.getHeaders();
System.out.println(headers);
// 获取请求体
String provinceJsonStr = entity.getBody();
System.out.println(provinceJsonStr);
return provinceJsonStr;
}
使用RequestEntity,将前端提交的Json字符串映射为Java对象(此时不能处理表单提交)
@RequestMapping("/demo05")
@ResponseBody
public Province demo05(RequestEntity<Province> entity) throws Exception {
// 获取请求头
HttpHeaders headers = entity.getHeaders();
System.out.println(headers);
// 获取请求体
Province province = entity.getBody();
System.out.println(province);
return province;
}
文件上传
表单的entype类型必须改为multipart/form-data,提交方式必须为post提交(因为post提交才有请求体)
依赖
<dependencies>
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--Json转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!--Tomcat依赖-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>8.5.41</version>
</dependency>
<!--文件上传依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
SpringMVC处理文件上传
配置文件上传解析器
<!--
配置文件上传的解析器
bean名称必须为: multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--文件上传时文件的最大大小(单位:字节)-->
<property name="maxUploadSize" value="102400000"></property>
<!--文件上传时,文件名称的编码-->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
demo01.jsp
<form action="/demo01/demo01" enctype="multipart/form-data" method="post">
<input type="file" name="pic">
<input type="submit">
</form>
Controller
@Controller
@RequestMapping("/demo01")
public class Demo01Controller {
@RequestMapping("/demo01")
public void demo01(
// 当表单项名称和形参名不一样可以使用@RequestPart注解来指定名称
@RequestPart("pic") MultipartFile pic,
HttpServletResponse response) throws Exception {
// 表单项的名称
String name = pic.getName();
// 文件的名称
String fileName = pic.getOriginalFilename();
// 文件的大小
long size = pic.getSize();
// 文件的MIME类型
String contentType = pic.getContentType();
// 将前端传递的文件写入到磁盘
pic.transferTo(new File("D:/000/" + fileName));
response.setContentType("text/html;charset=utf8");
response.getWriter().write("<hr/ >name: " + name);
response.getWriter().write("<hr/ >fileName: " + fileName);
response.getWriter().write("<hr/ >size: " + size);
response.getWriter().write("<hr/ >contentType: " + contentType);
}
}
多文件上传
form
<form action="/demo01/demo02" enctype="multipart/form-data" method="post">
<input type="file" name="pics">
<input type="file" name="pics">
<input type="file" name="pics">
<input type="file" name="pics">
<input type="submit">
</form>
Controller
@RequestMapping("/demo02")
@ResponseBody
public String demo02(MultipartFile[] pics) throws Exception {
for (MultipartFile file : pics) {
file.transferTo(new File("D:/000/" + UUID.randomUUID() + "-" + file.getOriginalFilename()));
}
return "success";
}
SpringMVC的异常处理
我们在处理异常时,通常使用try...catch块来处理程序中发生的异常;出现异常时每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。
SpringMVC提供的异常处理机制可以把异常处理抽离成方法、类;让处理异常的代码与我们的业务代码完全分开,降低与业务核心代码的耦合度;
Controller抛出异常
@Controller
@RequestMapping("/demo01")
public class Demo01Controller {
@RequestMapping("/demo01")
@ResponseBody
public String demo01(Integer flag) {
if (flag == 0) {
// 模拟异常
int i = 1 / 0;
}
return "success";
}
}
单异常处理
@ExceptionHandler
当控制器方法抛出特定类型的异常时,被@ExceptionHandler
注解标注的方法会被自动调用,以处理这些异常。这样可以将异常处理逻辑集中在一个地方,提高代码的可读性和可维护性。
@Controller
public class GlobalExceptionHandler {
@ExceptionHandler(YourException.class)
public ResponseEntity<String> handleYourException(YourException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
}
@ExceptionHandler只能处理该类或其内部抛出的异常
处理全局异常
编写一个类实现实现HandlerExceptionResolver接口来处理全局异常
@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println(handler.getClass());
System.out.println(ex.getClass());
ModelAndView mv = new ModelAndView();
mv.addObject("message", ex.getMessage());
mv.setViewName("/error.jsp");
return mv;
}
}
Tips:如果异常被Controller中的某个ExceptionHandler方法匹配了,那么不会进入自定义的异常解析器;
注解
-
@ControllerAdvice:注解可以配置全局异常
-
@ExceptionHandler(Exception.class):设置什么异常才会进入方法
@ControllerAdvice
public class AnnoMyExceptionHandler {
@ExceptionHandler(value = ArithmeticException.class)
public String arithmeticExceptionHandler(Model model, Exception e) {
model.addAttribute("message", e.getMessage());
return "forward:/error.jsp";
}
@ExceptionHandler(Exception.class)
public String exceptionHandler(Model model, Exception e) {
model.addAttribute("message", e.getMessage());
return "forward:/error.jsp";
}
}
Tips:
-
1)当同时定义了异常解析器(HandlerExceptionResolver)和@ControllerAdvice时,@ControllerAdvice优先级高;
-
2)如果@ControllerAdvice标注的异常和Controller方法定义的异常冲突,则以Controller中标注有@ExceptionHandler的方法优先;