0%

SpringMVC-全面了解

参数绑定,RequestMapping、返回值处理、拦截器、RESTful支持、跨域

SpringMVC-全面了解

处理请求映射,视图解析。对servlet做了封装。

执行流程:

  1. 用户发送请求至前端控制器DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  3. 处理器映射器找到具体的处理器返回给DispatcherServlet。
  4. DispatcherServlet调用HandlerAdapter处理器适配器。
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  6. Controller执行完成返回ModelAndView。
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  9. ViewReslover解析后返回具体View.
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet响应用户。

介绍

SpringMVC是spring-web模块的一部分。

六大组件

对外的三大组件

​ 前端控制器、处理器、视图

内部三大组件

处理器映射器、处理器适配器、视图解析器

前后端分离只需要开发处理器。

参数绑定

接收前端传递的参数后,SpringMVC依据绑定规则转换参数后,传递给Controller的形参。

SpringMVC底层采用24种绑定解析器(ArgumentResolver)处理

介绍

请求参数格式:

1
key/value

请求参数数据类型

1
String

请求参数要绑定的目标类型

1
Controller类中的方法参数,比如简单类型、POJO类型、集合类型等。

RequestParam注解

1
2
3
value:参数名字,即入参的请求参数名字
required:默认是true,表示请求中一定要有相应的参数
defaultValue:请求中没有同名参数时的默认值,使用时,required失效
1
public ModelAndView test(@RequestParam(value="name",required=false)String name){

绑定类型

简单类型数组

1
通过HTTP请求批量传递简单类型数组,Controller方法中可以用String[]或者pojo的String[]属性接收 (两种方式任选其一),但是不能使用List集合接收。
1
?id=1&id=2&id=3

POJO类型集合或数组

1
批量传递的请求参数,最终要使用List<POJO>来接收,List<POJO>必须放在另一个POJO类中

自定义日期 参数绑定

1
对于springmvc无法解析的参数绑定类型,需要自定义 [参数转换器]进行参数绑定。

Converter代码

1
2
3
4
5
6
7
8
9
10
11
12
public class DateConverter implements Converter<String, Date> { 
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}

Converter配置

1
2
3
4
5
6
7
<!-- 转换器配置 --> 
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters">
<set><
bean class="com.kkb.ssm.controller.converter.DateConverter"/>
</set>
</property>
</bean>

文件类型绑定

1.加入依赖 commons-fileupload

2.前端配置

1
JSP中的form表单需要指定enctype=”multipart/form-data”

3.配置Multipart解析器

1
2
3
4
5
<!-- multipart类型解析器,文件上传 --> 
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件的最大尺寸 5M-->
<property name="maxUploadSize" value="5242880"/>
</bean>

4.Cotroller类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("fileupload") 
public String findUserById(MultipartFile uploadFile) throws Exception {
if (uploadFile != null) {
System.out.println(uploadFile.getOriginalFilename());
// 原始图片名称
String originalFilename = uploadFile.getOriginalFilename();
// 如果没有图片名称,则上传不成功
if (originalFilename != null && originalFilename.length() > 0) {
// 存放图片的物理路径
String picPath = "E:\\";
// 获取上传文件的扩展名
String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
// 新文件的名称
String newFileName = UUID.randomUUID() + extName;
// 新的文件
File newFile = new File(picPath + newFileName);
// 把上传的文件保存成一个新的文件
uploadFile.transferTo(newFile);
// 同时需要把新的文件名更新到数据库中
// TODO
}
}
return "文件上传成功";
}

RequestMapping

value属性

1
2
// 用于映射URL和HandlerMethod方法
@RequestMapping(value="test")

method属性

1
2
3
// 限定请求URL使用指定的method请求方式
@RequestMapping(value="/test",method=RequestMethod.GET)
@RequestMapping(value="/test",method={RequestMethod.GET,RequestMethod.POST})

params属性

1
2
3
4
5
6
// 访问HandlerMethod 的限制
@RequestMapping(value="test",params= {"name","price>5000"})
public String test(Model model) {
model.addAttribute("msg", "test方法执行了");
return "success";
}

返回值处理

注解

@ResponseBody

1
针对Controller返回值类型,使用内置的9种HttpMessageConverter进行匹配,找 到合适HttpMessageConverter进行处理。

@RequestBody 处理请求参数的http消息转换

1
2
3
4
5
@ResponseBody
@RequestMapping()
public Message test(@RequestBody json){

}

非注解

1.ModelAndView

1
public ModelAndView test(){}

ModelAndView 可添加model数据、指定view。

2.void

1
public void test(HttpServletRequest request, HttpServletResponse response){}

request转发向页面

1
request.getRequestDispatcher("页面路径").forward(request, response);

过response页面重定向

1
response.sendRedirect("url")

response指定响应结果,例如响应json数据如下

1
response.setCharacterEncoding("utf-8"); response.setContentType("application/json;charset=utf-8"); response.getWriter().write("json串");

3.String

forward转发

1
return “forward:testForward";
1
2
3
4
5
相当于“ request.getRequestDispatcher().forward(request,response) ”

浏览器URL不发送改变

Request 域可以共享

拦截器

Spring MVC 的拦截器做了三次拦截。请求拦截一次,返回拦截两次。

请求拦截可做登录认证。

返回拦截中视图处理,前后端分离后不需要了

返回拦截中**,可用统一日志、统一异常处理。

定义拦截器

实现SpringMVC的HandlerIntercepter 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyHandlerIntercepter implements HandlerInterceptor{

//Handler执行前调用
//应用场景:登录认证、身份授权
//返回值为true则是放行,为false是不放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}

//进入Handler开始执行,并且在返回ModelAndView之前调用
//应用场景:对ModelAndView对象操作,可以把公共模型数据传到前台,可以统一指定视图
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

//执行完Handler之后调用
//应用场景:统一异常处理、统一日志处理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

配置拦截器

SpringMVC 拦截器是绑定在 HandlerMapping 中的,即:如果某个 HandlerMapping 中配置拦截,则该HandlerMapping 映射成功的 Handler 会使用该拦截器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 配置全局mapping的拦截器 --> 
<mvc:interceptors>
<!-- 公共拦截器可以拦截所有请求,而且可以有多个 -->
<bean class="com.ssm.interceptor.MyHandlerInterceptor" />
<bean class="com.ssm.interceptor.MyHandlerInterceptor2" />
<!-- 如果有针对特定URL的拦截器,则进行以下配置 -->
<mvc:interceptor>
<!-- /**表示所有URL和子URL路径 -->
<mvc:mapping path="/orders/**" />
<!-- 特定请求的拦截器只能有一个 -->
<bean class="com.ssm.interceptor.MyHandlerInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>

<!-- 如果有多个拦截器,那么配置到`springmvc.xml`中最上面的拦截器,拦截优先级最高。 -->

Restful支持

rest介绍

REST(英文:Representational State Transfer,意思是:(资源)表述性状态转化)

rest是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。

基于rest风格设计的软件更简洁,有层次,更易于实现缓存等机制。

资源

1
2
网络上的一个实体,例如:一段文本、一张图片、一首歌曲、一种服务。
可以用URI(统一资源定位符)指向它,每种资源对应一个特定的URI。

表现层

1
2
把资源呈现出来的形式,叫做表现层。
格式:html、xml、json,二级制。

状态转化

1
2
3
4
每发出一个HTTP请求,就代表了客户端和服务器的一次交互过程。
HTTP协议,是一个无状态协议,即所有的【状态】 都保存在服务器端。因此,如果客户端想要操作服务器, 必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化” 。

HTTP 协议里面,四个表示操作方式的动词:GET 、POST 、PUT 、DELETE 。它们分别对应四种 基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

如何设计RESTful应用程序的API

1
2
路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对于资源的路径也可以设计出来 
动词设计:也就是针对资源的具体操作类型,由HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET

RESTful的示例:

1
2
3
/account/1 HTTP GET : 得到 id = 1 的 account 
/account/1 HTTP DELETE: 删除 id = 1 的 account
/account/1 HTTP PUT: 更新 id = 1 的 account

SpringMVC如何支持Restful

配置

1
2
3
4
5
6
// 拦截RESTful请求
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
// 设置为/ ,前后端不分离时,需要处理静态资源路径。

url与参数绑定

@PathVariable

1
2
3
4
5
6
// 请求URL http://localhost:8080/ssm/item/1/lisi

// Controller
@RequestMapping(“{id}/{name}”)
@ResponseBody
public Item queryItemById(@PathVariable Integer id, @PathVariable String name){}

资源的表现形式

SpringMVC中可以使用 ContentNegotiatingManager这个内容协商管理器来控制表现形式

1
2
3
扩展名:比如.json表示我要JSON格式数据、.xml表示我要XML格式数据
请求参数:默认是”format”
请求头设置Accept参数:比如设置Accept为application/json表示要JSON格式数据

请求方式

1
2
@RequestMapping:通过设置method属性值
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解等同@RequestMapping注解配置method属性。

跨域

介绍

浏览器为了安全有这么一个规定:同源策略,即只用同源才可以访问浏览器的数据,非同源不可访问。

同源:相同的http协议、相同的域名、相同的端口号。

前后端分离后,前端和后端不是同源。这种情况我们要实现前后的非同源访问,就是跨域访问。

跨域情况
主域相同子域不同 http://www.baidu.com/a.js http://blog.baidu.com/b.js
协议不同 http://www.baidu.com/a.js https://www.baidu.com/b.js
域名对应真实ip http://www.baidu.com/a.js http://192.168.2.2/b.js

解决

三种方式:基于JavaScript标签的src方式,基于JQuery的JSONP方式,基于CORS的方式。

JSONP只能解决get方式。CORS可解决GET、POST。

CORS

介绍

CORS 是一个 W3C 标准,全称是”跨域资源共享”( Cross-origin resource sharing )。

它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求。

CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能, IE 浏览器不能低于 IE10

请求信息:

image-20200823082701342

响应信息:

image-20200823082826743

前端设置请求头。

1
自动向请求头 header 中注入 Origin 。

后端对响应拦截,设置响应头

1
服务器端需要向响应头 header 中注入 Access-Control-Allow-Origin
CORS实现跨域
CORS通过拦截器实现

跨域不提交cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AllowOriginInterceptor implements HandlerInterceptor { 
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 跨域行为参考网址 http://www.cnblogs.com/renhaisong/p/6892341.html
if (request.getHeader("Origin") != null) {
response.setContentType("text/html;charset=UTF-8");
// 允许哪一个URL
response.setHeader("Access-Control-Allow-Origin", "*");
// 允许那种请求方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("XDomainRequestAllowed", "1");
System.out.println("正在跨域");
}
return true;
}
}

跨域提交Cookie

注意:

  • Access-Control-Allow-Credentials 为 true 的时候, Access-Control-Allow-Origin 不能设置为”*”,否则报错。
  • 如果有多个拦截器,把处理跨域请求的拦截器放到首位

前端JQuery代码

1
2
3
4
5
6
7
8
9
10
11
12
13
$.ajax({ 
url: 'url',
method:'请求方式', //GET POST PUT DELETE
xhrFields:{
withCredentials:true
},
success:function(data){
// do something
},
error:function(){
// do something
}
})

后端JAVA代码,采用springMVC拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AllowOriginInterceptor implements HandlerInterceptor { 
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 有跨域行为时参考网址 http://namezhou.iteye.com/blog/2384434
if (request.getHeader("Origin") != null) {
response.setContentType("text/html;charset=UTF-8");
// 允许哪一个URL访问 request.getHeader("Origin") 动态获取请求来的url
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
// 允许那种请求方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,HEAD");
// 本次预检请求的有效期 ("0":每次异步请求都发起预检请求,允许跨域,再发出正式请求。"3600":有效期1小时)
response.setHeader("Access-Control-Max-Age", "0");
// 允许请求头里的参数列表
response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
// 允许对方带cookie访问
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("XDomainRequestAllowed", "1");
System.out.println("可接待cookie跨域");
}
return true;
}
}

后端JAVA代码,采用过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component // 表示这是配置类,注入到Spring容器中。(不属于@Controller、@Services、@repository)
public class CorsFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin")!=null ? httpRequest.getHeader("Origin"):"");
httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
chain.doFilter(request, response);
}

@Override
public void destroy() {

}
}
CORS通过注解方式

springMVC4.x以上:

1
@CrossOrigin