一、JSON解析方案

实际上:HttpMessageConverter起作用
功能:



  1. jackson
  • pom.xml(web下自带了json)
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

springboot dao转成JSON springboot jsonformat_CORS 跨域

  • 对日期进行格式化

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;
}
  1. 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();
    }
  1. 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;
    }
}

二、静态资源访问

  1. 自定义静态文件位置
  • 配置文件
#第一种方式:采用配置文件的方式
#源码在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/");
    }
}

三、文件上传

  1. 单文件上传
@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. 多文件上传
/**
     * 多文件上传
     * 情况: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

  1. 处理全局异常
  • 自定义全局异常
@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>
  1. 预设全局数据
  • 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";
    }
}
  1. 请求参数预处理
  • 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

五、异常数据处理

  1. 自定义错误页
  • 查看错误页面的规则(优先级):先动态后静态,先精确(404)后模糊(4xx)
  • 在静态/动态页面下,新建【error/状态码.html】,项目运行出现问题时会自动调用对应的html
  1. 自定义异常数据
  • 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>
  1. 自定义异常视图
  • 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

  1. 第一种方法
@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";
    }
}
  1. 第二种方法
@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>

六、拦截器

  1. 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");
    }
}
  1. WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPathPatterns:需要拦截的
        registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
    }

    @Bean
    MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }
}