文章目录
一个web请求经历的路径
浏览器(带有用户的请求信息)->dispatcherServlet(前端控制器)->处理器映射(handler mapping,查询url中映射的控制器)->dispatcherServlet(获取控制器)->控制器(也叫处理器,将返回信息包装为model并标注逻辑视图名称)->dispatcherServlet(通过逻辑试图名称在解析器中查找对应的视图)->视图(视图一般是jsp或其他模板引擎,模板引擎将信息进行格式化(一般是html))->dispatcherServlet->浏览器(最终的响应)
个人认为,这里的处理器映射器是将url请求映射到具体的handler上(这里牵扯最早的一个处理器上只能有一个方法,有点像servlet接口,一个servlet只能响应一个请求);至于处理器适配器就是将这个请求适配到我们现在所使用的控制层多方法上,所以叫适配器(就是将原先的只能处理一个url请求的控制器适配成现在可以处理多个url请求的控制器),处理器适配器可以先不关心,不影响对url请求映射的理解。
springmvc的基本配置
需要引入的jar包
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
使用AbstractAnnotationConfigDispatcherServletInitializer替代web.xml文件
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {// 驱动中间层和数据层组件的配置文件
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {// 此处设置web配置文件比如controller层
return new Class<?>[] { Webconfig.class };
}
@Override
protected String[] getServletMappings() {//设置dispatcherServlet的根路径
return new String[] { "/" };
}
}
WebConfig.java
WebMvcConfigurerAdapter 在高版本的spring中已经废弃,但是配置中的方法不变,替代的方案是
public class Webconfig implements WebMvcConfigurer{
}
或
public class Webconfig extends WebMvcConfigurationSupport{
}
这里以WebMvcConfigurerAdapter 为例,WebMvcConfigurer和WebMvcConfigurationSupport中的方法名称和它一样
@Configuration
@EnableWebMvc // 开启spring mvc配置
@ComponentScan("springmvc.base.controller")
public class Webconfig extends WebMvcConfigurerAdapter {
/**
* 定义jsp视图解析器
* @return
*/
@Bean
public InternalResourceViewResolver viewResolver() {// 定义一个视图解析器
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");// 设置前缀,此前缀即在 src/main/resources/views路径下
viewResolver.setSuffix(".jsp");// 设置后缀,此处解析的是jsp视图
viewResolver.setExposeContextBeansAsAttributes(true);// 设置jsp能否访问后端的bean
return viewResolver;
}
//定义静态资源映射,比如js文件和图片
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").// 设置静态文件url
addResourceLocations("classpath:/assets/");// 设置静态文件类路径
}
/**
* 定义静态视图解析器,将jsp直接映射为url
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
RootConfig.java
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
//排除EnableWebMvc注解下的所描路径
@ComponentScan(basePackages = { "springmvc.base" }, excludeFilters = {
@Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) })
public class RootConfig {
}
springmvc的控制器
基本控制器
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller // 声明一个控制器
public class HelloController {
@RequestMapping(value="/",method=RequestMethod.GET) // get请求url->"/"
public String hello() {
return "index";// 视图名称
}
}
model参数控制器
@RequestMapping(value="/model",method=RequestMethod.GET) // get请求url->"/"
public String model(Model model) {
model.addAttribute("demoObj", demoObj);
return "index";// 视图名称
}
处理一般请求参数
@RequestMapping(value = "/normalPara", method = RequestMethod.GET) // get请求url->"/normalPara?id=1&name=abc"
public String normalPara(int id, String name) {
System.out.println(id + "," + name);
return "index";// 视图名称
}
/**
* 请求http://localhost:8080/springmvc/anno/paramBody?zklName=zkl
* 返回结果:url:http://localhost:8080/springmvc/anno/paramBody can access!name:zkl
* @param request
* @return
*/
@RequestMapping(value = "/paramBody") // produces定义媒体类型为文本,字符集为UTF-8
@ResponseBody
public String paramBody(@RequestParam(defaultValue = "zhangkelong", required = true, name = "zklName") String name,
HttpServletRequest request) {// 设置默认值、必选参数和参数名称
return "url:" + request.getRequestURL() + " can access!name: " + name;
}
处理request参数
/**
* 请求url:http://localhost:8080/springmvc/anno/httpRequest
* 返回结果:url:http://localhost:8080/springmvc/anno/httpRequest can access!
*
* @param request
* @return
*/
@RequestMapping(value = "/httpRequest", produces = "text/plain;charset=UTF-8") // produces定义媒体类型为文本,字符集为UTF-8
@ResponseBody // 定义此方法返回字符串
public String index(HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access!";
}
处理路径参数
/**
* 演示路径参数定义
* 请求url:http://localhost:8080/springmvc/anno/pathvar/zkl
* 返回结果:url:http://localhost:8080/springmvc/anno/pathvar/zkl can access!str:
* zkl
*
* @param str
* 路径参数
* @param request
* @return
*/
@RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8") // produces定义媒体类型为文本,字符集为UTF-8
@ResponseBody // 定义此方法返回字符串
public String demoPathVar(@PathVariable String str, HttpServletRequest request) {// @PathVariable标志一个
// 参数为路径参数
return "url:" + request.getRequestURL() + " can access!str: " + str;
}
多个路径访问同一个controller
/**
* 演示不同路径到相同的方法
* 请求url1:http://localhost:8080/springmvc/anno/mulPath1
* 返回结果1:url:http://localhost:8080/springmvc/anno/mulPath1 can access!
*
* 请求url1:http://localhost:8080/springmvc/anno/mulPath2
* 返回结果1:url:http://localhost:8080/springmvc/anno/mulPath2 can access!
*
* @param request
* @return
*/
@RequestMapping(value = { "/mulPath1", "/mulPath2" }, produces = "text/plain;charset=UTF-8") // 声明此方法的映射路径为:和http://localhost:8080/springmvc/anno/mulPath2;produces定义媒体类型为文本,字符集为UTF-8
@ResponseBody // 定义此方法返回字符串
public String mulPath(HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access!";
}
控制器的redirect和forward
forward:服务器端跳转,因为是同一个请求所以跳转之间参数是可以无缝传递的(包含基本类型和对象)
//跳转页面的例子
@RequestMapping(value = "/forward", method = RequestMethod.GET)
public String forward() {
return "forward:/staticfile";
}
//跳转带参数的例子
@RequestMapping(value = "/forwardWithPara", method = RequestMethod.GET)
public String forwardWithPara(String name) {
return "forward:/forwardDst";
}
@RequestMapping(value = "/forwardDst", method = RequestMethod.GET)
@ResponseBody
public String forwardDst(String name) {
return name;
}
redirect:流览器端跳转,因为是两个请求所以参数不能无缝传递,可以使用RedirectAttributes传递,不过也只能给页面传递,RedirectAttributes不仅可以传递基本类型,也可以传递对象
@RequestMapping(value = "/redirect", method = RequestMethod.GET)
public String redirect() {
return "redirect:/staticfile";
}
@RequestMapping(value = "/redirectWithPara", method = RequestMethod.GET)
public String redirectWithPara(String name, RedirectAttributes model) {
model.addFlashAttribute("name", name);
return "redirect:/redirectDst";
}
@RequestMapping(value = "/redirectDst", method = RequestMethod.GET)
public String redirectDst() {
return "redirectpage";
}
//这里的name是无法接收到参数的
@RequestMapping(value = "/redirectDst1", method = RequestMethod.GET)
@ResponseBody
public String redirectDst1(String name) {
return name;
}
redirectpage.jsp:页面可以接收到参数
</head>
<body>
${name}
</body>
</html>
处理复杂参数(容器)
处理请求基本类型参数,如字符串,整形等
前端:
var arr = [ '1', '2', '3' ];
$.ajax({
type : 'get',
url : 'collectionPara/postListString',
dataType : 'json',
data : {
ids : arr
},
success : function(data) {
console.log(JSON.stringify(data));
}
})
后端:
@Controller
@RequestMapping("/collectionPara")
public class CollectionParaController {
/**
* 使用以下url直接get也行 collectionPara/postListString?ids[]=1&ids[]=2&ids[]=3
* @param ids
* @return
*/
@RequestMapping("/postListString")
@ResponseBody
public List<String> postListString(@RequestParam(value = "ids[]") List<String> ids) {
return ids;
}
}
处理请求参数为容器中包含对象
前端:
var testList = [];
var user1 = {};
user1.id = 1;
user1.name = 'jack';
testList.push(user1);
var user2 = {};
user2.id = 2;
user2.name = 'tom';
testList.push(user2);
$.ajax({
// headers必须添加,否则会报415错误
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
type : 'POST',
dataType : "json", //表示返回值类型,不必须
data : JSON.stringify(testList),//这里的参数名称可以可以控制器参数名称不一样,因为是直接将所有数据提交到后端的
url : 'collectionPara/postListObject',
success : function(data) {
console.log(JSON.stringify(data));
}
});
后端:
/**
* 这里只能使用post方式进行提交数据才行,必须使用RequestBody
* @param students 此处的参数名称不影响任何东西
* @return
*/
@RequestMapping("/postListObject")
@ResponseBody
public List<Student> postListObject(@RequestBody List<Student> students) {
return students;
}
将参数不放入对象而放入map中将域的值转化为名值对
前端:
var userList = new Array();
userList.push({
name : "张三",
pwd : "123"
});
userList.push({
name : "李四",
pwd : "223"
});
$.ajax({
type : "POST",
url : "collectionPara/mapPara",
data : JSON.stringify(userList),//将对象序列化成JSON字符串
dataType : "json",
contentType : 'application/json;charset=utf-8', //设置请求头信息
success : function(data) {
console.log(JSON.stringify(data));
}
});
后端:
/**
* 不使用对象接受数据,直接使用map接受(相当于将对象的域和值直接变为名值对)
* @param listMap
* @return
*/
@RequestMapping("/mapPara")
@ResponseBody
public List<Map<String, Object>> mapPara(@RequestBody List<Map<String, Object>> listMap) {
return listMap;
}
请求参数中即有容器也有一般属性
前端:
var userArray = new Array();
userArray.push({
id : 1,
name : "张三"
});
userArray.push({
id : 2,
name : "李四"
});
var user = {};
user.name = "王五";
user.pwd = "888";
user.students = userArray;
$.ajax({
type : "POST",
url : "collectionPara/listObject",
data : JSON.stringify(user),//将对象序列化成JSON字符串
dataType : "json",
contentType : 'application/json;charset=utf-8', //设置请求头信息
success : function(data) {
console.log(JSON.stringify(data));
}
});
后端:
@RequestMapping("/listObject")
@ResponseBody
public ListObject listObject(@RequestBody ListObject listObject) {
return listObject;
}
处理控制器抛出的异常
如果我们需要将控制器的异常映射到某一自定义界面上,往往需要try catch然后再catch中返回此异常对应的界面,spring提供了无需try catch的方式
@RequestMapping(value = "/notExistException", method = RequestMethod.GET)
public String notExistException() {
throw new NotExistException("抛出异常");// 直接在异常上定义此异常抛出时的错误码和返回消息
}
// 此处定义局部异常处理,此局部异常处理可以将本controller中所有抛出NotExistException的地方映射为error错误页面
@ExceptionHandler(value = NotExistException.class)
public ModelAndView HandleNotExistException(Exception exception, WebRequest request) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("errormessage", exception.getMessage());
return modelAndView;
}
除了局部设置异常处理外,这里还有一种全局设置的方式,可以将所有controller中抛出的同一类型异常映射为特定错误页面,这里如果全局异常和局部异常处理的是同一种异常,则优先使用局部异常处理,全局异常处理定义并不起任何作用
@ControllerAdvice // 声明一个控制器建言
public class ExceptionHandlerAdvice {
@ExceptionHandler(value = Exception.class) // 此处定义全局处理,通过value属性可以定义拦截条件,此处拦截所有异常
public ModelAndView exception(Exception exception, WebRequest request) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("errormessage", exception.getMessage());
return modelAndView;
}
}
还有一种方式是将异常映射为错误码,这种方式比较简单,只需要在异常定义的地方进行处理即可,控制层按照正常方式抛出异常即可
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "not found something")
public class NotFoundException extends RuntimeException {
private static final long serialVersionUID = -3993942627519471418L;
}
添加全局消息
有时候我们需要给所有控制器添加同一种消息
@ControllerAdvice // 声明一个控制器建言
public class ExceptionHandlerAdvice {
@ModelAttribute // 将键值对添加到全局,所有@RequestMapping的方法都可以获得此键值对
public void addAttributes(Model model) {
model.addAttribute("msg", "gloable message");
}
}
@Controller
public class GloableMessageController {
// url为 http://localhost:8080/springmvc/gloableMessage?id=1
// 此url中并没有带msg,但msg已经被赋值了,全局消息需要使用@ModelAttribute注解来接收
@RequestMapping("/gloableMessage")
@ResponseBody
public String gloableMessage(@ModelAttribute("msg") String msg, Integer id) {
return "id:" + id + "," + "msg:" + msg;
}
}
添加全局忽略消息
有一些消息许多url中都会带,但是我们不希望控制层能接收到此消息,可以使用全局忽略,在控制器建言中设置,如果控制器参数是通过固定属性而非对象属性接收,则不能忽略固定属性
@ControllerAdvice // 声明一个控制器建言
public class ExceptionHandlerAdvice {
@InitBinder // 注解定制WebDataBinder
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.setDisallowedFields("ignore");// 设置忽略的属性
}
}
public class DemoObj {
private int id;
private String name;
private String ignore;// 被全局定义忽略的属性
//get set toString 方法...
}
@Controller
public class GloableMessageController {
// url为
// http://localhost:8080/springmvc/ignoreMessage?id=1&name=n1&ignore=aaa
// 此url中带ignore,但ignore被忽略了
@RequestMapping("/ignoreMessage")
@ResponseBody
public String ignoreMessage(DemoObj obj) {
return obj.toString();
}
// url为 http://localhost:8080/springmvc/ignoreMessage1?id=1&ignore=aaa
// 此url中带ignore,但ignore并没有被忽略
@RequestMapping("/ignoreMessage1")
@ResponseBody
public String ignoreMessage1(Integer id, String ignore) {
return "id:" + id + "," + "ignore:" + ignore;
}
}
扩展配置,自定义配置
在AbstractAnnotationConfigDispatcherServletInitializer的父类AbstractDispatcherServletInitializer有一个方法customizeRegistration(Dynamic registration)方法可以扩展dispatcherServlet
@Override
protected void customizeRegistration(Dynamic registration) {
super.customizeRegistration(registration);
}
添加servlet、filter和listener
方式一
创建一个新的初始化器,java配置可以创建任意多个初始化器和AbstractAnnotationConfigDispatcherServletInitializer并不冲突
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
/**
* 创建一个新的初始化器,这个和AbstractAnnotationConfigDispatcherServletInitializer并不冲突
*/
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 添加一个普通的servlet 访问url:http://localhost:8080/springmvc/normalServlet
Dynamic normalServlet = servletContext.addServlet("normalServlet", new NormalServlet());
normalServlet.addMapping("/normalServlet");
normalServlet.setLoadOnStartup(1);
// 添加一个普通的listener
servletContext.addListener(NormalListener.class);
// 添加一个普通的filter
FilterRegistration.Dynamic normalFilter = servletContext.addFilter("normalFilter", NormalFilter.class);// 传入参数为filter名称和filter的定义类
normalFilter.addMappingForUrlPatterns(null, true, "/*");// 定义filter的适用url
}
}
filter的第二种设置方法
public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
//此处的filter都会映射到DispatcherServlet上
@Override
protected Filter[] getServletFilters() {
return new Filter[]{new NormalFilter()};
}
}
方式二
还可以利用AbstractDispatcherServletInitializer的onStartup方法,AbstractAnnotationConfigDispatcherServletInitializer继承于AbstractDispatcherServletInitializer,所以在AbstractAnnotationConfigDispatcherServletInitializer中直接重写并且一定要使用super.onStartup(servletContext)方法,因为里边有初始化applicationContext的方法
public class MVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 这里一定要使用super.onStartup(servletContext)方法,因为里边有初始化applicationContext的方法
*/
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
// 给此处添加Listener、Filter、servlet
}
}
multipart上传文件
在Webconfig.java中配置MultipartResolver
@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
// 如果使用的是servlet3.0以下的版本使用CommonsMultipartResolver需要引入commons-fileupload包
// @Bean
// public MultipartResolver multipartResolver() {
// CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
// multipartResolver.setDefaultEncoding("UTF-8");
// multipartResolver.setMaxInMemorySize(50);// 使用内存的大小
// multipartResolver.setMaxUploadSize(500000);// 设置上传文件大小
// return multipartResolver;
// }
在SpringWebInitializer.java中进行自定义配置文件路径和大小,如果是MultipartResolver不需要在这里再次配置
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement("D:\\", 50 * 1024 * 1024, 100 * 1024 * 1024, 0));
}
}
控制层使用MultipartFile或者Part接收文件
import java.io.File;
import java.io.IOException;
import javax.servlet.http.Part;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class UploadController {
//使用MultipartFile接收文件时需要配置MultipartResolver
@RequestMapping(value = "/uploadFile1", method = RequestMethod.POST)
@ResponseBody
public String upload1(MultipartFile fileWarName) {// MultipartFile接受上传文件
try {
//写文件方式一
FileUtils.writeByteArrayToFile(new File("D:/upload/" + fileWarName.getOriginalFilename()),
fileWarName.getBytes());
//写文件方式二,使用MultipartFile的transferTo
//fileWarName.transferTo(new File("D:/upload/" + fileWarName.getOriginalFilename());
return "ok";
} catch (IOException e) {
e.printStackTrace();
return "wrong";
}
}
//使用Part接收文件时不需要配置MultipartResolver
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestPart("fileWarName") Part fileWarName) {
try {
fileWarName.write("D:/upload/" + fileWarName.getName());
return "ok";
} catch (IOException e) {
e.printStackTrace();
return "wrong";
}
}
}
前端写法
注意属性enctype=“multipart/form-data”
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=ISO-8859-1">
<title>upload.jsp</title>
</head>
<body>
本jsp演示文件上传下载
<form action="uploadFile" enctype="multipart/form-data" method="post">
<input type="file" name="fileWarName"/><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
下载文件
@Controller
@RequestMapping("/downloadFile")
public class DownloadController {
@RequestMapping("/download")
public ResponseEntity<byte[]> download() throws IOException {
String path = "D:"+File.separator+"test.txt";
File file = new File(path);
if (file.exists()) {
System.out.println("true");
}
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", "test");
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);//使用application/octet-stream
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
}
}
路径中“.”号的识别设置
public class Webconfig extends WebMvcConfigurerAdapter {
/**
* 设置路径参数匹配 spring mvc默认路径忽略路径参数中"."后的所有参数
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
/**
* 设置为false表示不忽略,默认或者设置为true表示忽略
*/
configurer.setUseSuffixPatternMatch(false);
}
}
mvc测试
package springmvc.base.test;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import springmvc.base.Webconfig;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { Webconfig.class })
@WebAppConfiguration("src/main/resources")
public class TestControllerIntegrationTests {
private MockMvc mockMvc;
@Autowired
private DemoService demoService;
@Autowired
private WebApplicationContext wac;
// session和request需要的时候再注入
// @Autowired
// private MockHttpSession session;
// @Autowired
// private MockHttpServletRequest request;
@Before
public void setup() {// 初始化mockMvc
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testNormalController() throws Exception {
mockMvc.perform(get("/normal")).andExpect(status().isOk()).andExpect(view().name("page"))
.andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))
.andExpect(model().attribute("msg", demoService.saySomething()));
}
@Test
public void testRestController() throws Exception {
mockMvc.perform(get("/testRest")).andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=UTF-8"))
.andExpect(content().string(demoService.saySomething()));
}
@Test
public void testAysncController() throws Exception {
Object object = mockMvc.perform(get("/defer").param("user", "zhangkelong"))
.andExpect(status().isOk())
.andReturn().getAsyncResult();
System.out.println(object);
}
}
跨域问题
CROS常见header
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3628800
Access-Control-Allow-Methods: GET,PUT, DELETE,POST
Access-Control-Allow-Headers: content-type
"Access-Control-Allow-Origin"表明它允许任意网站发起跨域请求
"Access-Control-Max-Age"表明在3628800秒内,不需要再发送预检验请求,可以缓存该结果(CROS协议中,一个AJAX请求被分成了第一步的OPTION预检测请求和正式请求)
"Access-Control-Allow-Methods"表明它允许GET、PUT、DELETE,POST的跨域请求
"Access-Control-Allow-Headers"表明它允许跨域请求包含content-type头
@CrossOrigin(origins = “*”)
全局解决:当需要应用的所有请求都跨域的时候在WebMvcConfigurer中进行配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/baseurl/*").allowedOrigins("*");
}