一、JSON解析方案
实际上:HttpMessageConverter起作用
功能:
- jackson
- pom.xml(web下自带了json)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 对日期进行格式化
public class User {
private Integer id;
private String name;
private String address;
//第一种方式:json日期格式化。该方法只在jackson中有效
//弊端:要在每个实体类日期属性上加
//@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
- 第二种方法:MappingJackson2HttpMessageConverter
注:若是需要设置编码的格式,还是需要该方式
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
//第二种方式:对日期进行格式化(源码在JacksonHttpMessageConvertersConfiguration中的MappingJackson2HttpMessageConverter)
//原则上是ObjectMapping在起作用
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
objectMapper.setDateFormat(dateFormat);
converter.setObjectMapper(objectMapper);
return converter;
}
@Bean
ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return objectMapper;
}
- gson
- pom.xml
注:需要先把web中的json去除
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 导入gson依赖,不需要版本号,因为spring-boot-dependencies中导入了版本号-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
- 时间格式化
//第一种方式:源码在GsonHttpMessageConvertersConfiguration.class中的GsonHttpMessageConverter
@Bean
GsonHttpMessageConverter gsonHttpMessageConverter() {
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(new GsonBuilder().setDateFormat("yyyy-MM-dd").create());
return converter;
}
@Bean
Gson gson() {
return new GsonBuilder().setDateFormat("yyyy/MM/dd").create();
}
- fastjson
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--导入fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
- FastJsonHttpMessageConverter(必须定义)
@Configuration
public class WebMvcConfig {
//fastjson:必须要自定义一个FastJsonHttpMessageConverter
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//设置编码格式
/* List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(fastMediaTypes);*/
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setDateFormat("yyyy-MM-dd");
converter.setFastJsonConfig(fastJsonConfig);
return converter;
}
}
二、静态资源访问
- 自定义静态文件位置
- 配置文件
#第一种方式:采用配置文件的方式
#源码在WebMvcAutoConfiguration.class
#自定义静态资源的位置。默认五个:"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/","/"(最后一个/代表在webapp下)
spring.resources.static-locations=classpath:/lyl/
#静态资源中的内容将采用/hello/**(具体文件)进行访问。默认是/**
spring.mvc.static-path-pattern=/hello/**
- java代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//第二种:采用java的方式进行静态资源的访问
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/hello/**").addResourceLocations("classpath:/lyl/");
}
}
三、文件上传
- 单文件上传
@RestController
public class FileUploadController {
//上传文件需要注意:
//1.上传的文件需要分类,不然文件过多,会导致加载很慢
//2.文件上传要限制大小
//3.重新命名文件名
@PostMapping("/upload")
public String upload(MultipartFile file, HttpServletRequest request) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("/yyyy-MM-dd/");
//获取传入的文件名
String originalFilename = file.getOriginalFilename();
//获取重命名的文件名
String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//存取文件的位置
String dateFormat = simpleDateFormat.format(new Date());
//request.getServletContext().getRealPath("/img"):返回的是一个临时文件的路径
String filePath = request.getServletContext().getRealPath("/img") + dateFormat;
File folder = new File(filePath);
//判断文件夹是否存在
if(!folder.exists()) {
folder.mkdirs();
}
try {
file.transferTo(new File(folder,filename));
String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/img" + dateFormat + filename;
return url;
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
!application.properties
#单个文件上传大小。MB需要大写
spring.servlet.multipart.max-file-size=1MB
!表单的方式:
static/index.html
<!--静态资源存放的五个位置可以直接访问文件名。因为制定了addResourceHandle("/**")。
访问:localhost:8080/index.html
注:表单必须为post请求、enctype="multipart/form-data"、输入框的type为file、name必须与控制器的形参对应。
-->
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
!ajax的方式
<head>
<meta charset="UTF-8">
<title>ajax进行文件上传</title>
//引入jquery
<script src="jquery-3.5.1.js"></script>
</head>
<body>
<div id="result"></div>
<input type="file" id="file">
<input type="button" value="上传" onclick="upload()">
<script>
function upload() {
var file = $("#file")[0].files[0];
var formData = new FormData();
formData.append("file",file);
//processData:默认是true。是否将上传的数据处理为对象
//contentType:设置请求头。该处false:是避免让jquery去设置请求头,因为会破坏分隔符(用来确认上传文件的起始位置)
$.ajax({
type:'post',
url:"/upload",
processData: false,
contentType:false,
data: formData,
success:function(msg) {
$('#result').html(msg);
}
})
}
</script>
</body>
- 多文件上传
/**
* 多文件上传
* 情况:1.多个input。2.一个input上传多个文件
* @param files
* @param request
* @return
*/
@PostMapping("/uploads")
public String uploadS(MultipartFile[] files, HttpServletRequest request) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("/yyyy-MM-dd/");
//存取文件的位置.
String dateFormat = simpleDateFormat.format(new Date());
//request.getServletContext().getRealPath("/img"):返回的是一个临时文件的路径
String filePath = request.getServletContext().getRealPath("/img") + dateFormat;
File folder = new File(filePath);
//判断文件夹是否存在
if(!folder.exists()) {
folder.mkdirs();
}
for (MultipartFile file : files) {
//获取传入的文件名
String originalFilename = file.getOriginalFilename();
//获取重命名的文件名
String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
try {
file.transferTo(new File(folder,filename));
String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/img" + dateFormat + filename;
System.out.println(url);
} catch (IOException e) {
e.printStackTrace();
}
}
return "success";
}
!多个input
<form action="/uploads" method="post" enctype="multipart/form-data">s
<!-- name的属性要与/uploads对应的那个控制器的形参一致-->
<input type="file" name="files"><br>
<input type="file" name="files"><br>
<input type="submit" value="提交">
</form>
!一个input上传多个文件
<form action="/uploads" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple>
<input type="submit" value="提交">
</form>
四、@ControllerAdvice
- 处理全局异常
- 自定义全局异常
@ControllerAdvice
public class MyException {
//使用基本的信息输出
/* @ExceptionHandler(MaxUploadSizeExceededException.class)
public void exception(MaxUploadSizeExceededException m, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("上传文件大小超出限制");
out.flush();
out.close();
}*/
//使用静态页面进行错误输出
//thymeleaf模板默认是在templates下
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView exception(MaxUploadSizeExceededException m) throws IOException {
ModelAndView modelAndView = new ModelAndView("MyError");
modelAndView.addObject("error","上传文件大小超出限制");
return modelAndView;
}
}
---classpath:/templates/MyError.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${error}"></h1>
</body>
</html>
- 预设全局数据
- GlobalData .java
@ControllerAdvice
public class GlobalData {
//@ControllerAdvice第二种用法:预设全局数据
//在当前项目的任何一个controller中都能获取得到
//获取到的是一个map,其中key为info,value为maps
@ModelAttribute(value = "info")
public Map<String,Object> myData() {
Map<String, Object> maps = new HashMap<>();
maps.put("name","蘑菇");
maps.put("address","重庆");
return maps;
}
- HelloController
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
//获取到GlobalData中的数据
Map<String, Object> map = model.asMap();
Set<String> set = map.keySet();
for (String string : set) {
System.out.println(string + ":" + map.get(string));
}
return "hello";
}
}
- 请求参数预处理
- GlobalData
@ControllerAdvice
public class GlobalData {
//@ControllerAdvice第三种用法:请求参数预处理
//@InitBinder的内容与@ModelAttribute()的内容对应
@InitBinder("a")
public void initA(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
@InitBinder("b")
public void initB(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
}
- BookController
@PostMapping("/book")
public void book(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
System.out.println(book);
System.out.println(author);
}
测试时:输入b.name,b.price
a.name,a.age
五、异常数据处理
- 自定义错误页
- 查看错误页面的规则(优先级):先动态后静态,先精确(404)后模糊(4xx)
- 在静态/动态页面下,新建【error/状态码.html】,项目运行出现问题时会自动调用对应的html
- 自定义异常数据
- MyDefaultAttribute
//自定义异常数据
//注册了@Component ,则源码ErrorMvcAutoConfiguration中的DefaultErrorAttribute就会失效。
@Component
public class MyDefaultAttribute extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
map.put("myError","自定义异常处理数据");
return map;
}
}
- classpath:resources/error/500.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>500</title>
</head>
<body>
<table border="1">
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>myError</td>
<td th:text="${myError}"></td>
</tr>
</table>
<h1>500</h1>
</body>
</html>
- 自定义异常视图
- MyErrorViewResolver
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
super(applicationContext, resourceProperties);
}
//自定义结果视图
//Map<String, Object> model:是一个不可修改的map
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView mv = new ModelAndView();
//lyl.html需要放在templates下面
mv.setViewName("lyl");
mv.addAllObjects(model);
return mv;
}
}
- classpath:resources/error/lyl.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>自定义异常视图</title>
</head>
<body>
<h1>自定义异常视图</h1>
<table border="1">
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>myError</td>
<td th:text="${myError}"></td>
</tr>
</table>
<h1>lyl</h1>
</body>
</html>
六、Cors
- 第一种方法
@RestController
public class HelloController {
//第一种方式
//允许cors2(端口8081)进行访问.
//@CrossOrigin:可以放在方法和类上.
//缺点:多个controller要写多次
//愿意接收来自http://localhost:8081的请求
//@CrossOrigin(origins = "http://localhost:8081")
@GetMapping("/get")
public String get() {
return "hello get";
}
@PutMapping("/put")
public String put() {
return "hello put";
}
}
- 第二种方法
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 配置跨域请求
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
//第二种方式:仅需设置一次
//addMapping:哪些接口允许跨域
//maxAge:有效时间(例:如果发送的请求是PUT请求,那么会发送两次,第一次发送PUT,是一个探测请求,发送到服务器判断是否支持PUT请求
// ,存在就发送第二次请求。maxAge代表的是探测请求的有效时间,在有效时间内,就不用发送探测请求了。
// )
registry.addMapping("/**").allowedOrigins("http://localhost:8081")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(30*1000);
}
}
- Cors2访问Cors1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cors</title>
<script src="jquery-3.5.1.js"></script>
</head>
<body>
<div id="app"></div>
<input type="button" value="GET" onclick="getData()">
<input type="button" value="PUT" onclick="putData()">
<script>
function getData() {
$.get("http://localhost:8080/get",function(msg) {
$('#app').html(msg);
});
}
function putData() {
$.ajax({
type:'put',
url:"http://localhost:8080/put",
success:function(msg) {
$('#app').html(msg)
}
})
}
</script>
</body>
</html>
六、拦截器
- MyInterceptor
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//return true :以下两个方法才能执行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
- WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns:需要拦截的
registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
}
@Bean
MyInterceptor myInterceptor() {
return new MyInterceptor();
}
}