Spring MVC 的高级技术

一、处理 multipart 形式的数据

1、配置multipart解析器

Spring内置的两个解析器:

  • CommonsMultipartResolver :使用Jakarta Commons FileUpload 解析
  • StandardServletMultipartResolver :依赖于Servlet 3.0 对multipart请求的支持(始于Spring 3.1)

配置 StandardServletMultipartResolver multipart 解析器

配置 StandardServletMultipartResolver必须指定文件上传的临时目录,即必须在web.xml 或Servlet 初始化类中,将 multipart的具体细节作为DispatcherServlet 配置的一部分。

如果配置 DispatcherServlet 的 Servlet 初始化类实现了 WebApplicationInitializer,可以在 Servlet registration 上调用 setMultipartConfig() 方法,传入一个 MultipartConfig - Element 实例。

   @Override
   public void onStartup(ServletContext servletContext) throws ServletException {
   
       DispatcherServlet dispatcherServlet = new DispatcherServlet();
       ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcherServler", ds);
       registration.addMapping("/");
       registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
   }

如果初始化类继承了AbstractAnnotationConfigDispatcherServletInitializer 或者 AbstractDispatcherServletInitializer,可以通过重载 customizeRegistration() 来配置 multopart 的具体细节。

   @Override
   protected void customizeRegistration(ServletRegistration.Dynamic registration) {
       registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
   }

xml 配置

 <servlet>
       <servlet-name>springDispatcherServletservlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet
       servlet-class>
       <load-on-startup>1load-on-startup>
       <multipart-config>
           <location>/tmp/uploadslocation>
           <max-file-size>2097152max-file-size>
           <max-request-size>4194304max-request-size>
       multipart-config>
   servlet>

配置 CommonsMultipartResolver multipart 解析器

通常 StandardServletMultipartResolver 是最佳的选择,如果应用部署到非Servlet 3.0的容器中,则可以使用此代替。CommonsMultipartResolver不强制设置临时文件路径,默认临时文件路径为 Servlet 容器的临时目录。

	/**
     * 配置 multipart 解析器
     */
    @Bean
    public MultipartResolver multipartResolver() throws IOException {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        //默认临时文件路径为 Servlet 容器的临时目录
        commonsMultipartResolver.setUploadTempDir(new FileSystemResource("/temp/uploads"));
        commonsMultipartResolver.setMaxUploadSize(2097152);
        commonsMultipartResolver.setMaxInMemorySize(0);
        return commonsMultipartResolver;
    }

2、处理 multipart 请求

接受MultipartFile
Spring 提供了 MultipartFile 接口,不仅可以获取上传文件byte,还能获取原始文件名、大小及文件内容,还提供了 InputStream,用来将文件以流的方式读取。
除此,还提供了一个 transferTo() 方法,能够将上传的文件写入到文件系统中,例如:

myFile.transferTo(new File("/file/uploads/" +myFile.getOriginalFilename()));

以Part的形式接受上传的文件
在编写控制器的时候,如果以 Part 参数的形式接受文件上传,就不需要配置 MultipartResolver ,只有在使用 MultipartFile 的时候才需要配置。

二、处理异常

Spring 提供了多种方式将异常转换为响应:

  • 特定的 Spring 异常将会自动映射为指定的 Http 状态码;
  • 异常上可以添加 @ResponseStatus 注解,从而将其映射为一个 Http 状态码;
  • 在方法上可以添加 @ExceptionHandler 注解,使其用来处理异常。

1、将异常映射为Http 状态码

除了Spring 内置的映射,还可以通过注解映射,例如:

@ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "user not found")
public class UserNotFoundException extends RuntimeException{
}

当抛出 UserNotFoundException 异常时,响应将具有 404 状态码。

2、编写异常处理的方法

如果在响应中需要包含所产生的错误,就不能讲异常视为 HTTP 错误,而是需要根据处理请求的方式来处理异常。

	@ExceptionHandler({UserNotFoundException.class})
    @ResponseBody
    public String handleUserNotFound(){
        return  "user not found";
    }

@ExceptionHandler 注解所标注的方法,能处理同一个控制器中所有处理器方法所抛出的指定异常。

3、为控制器添加通知

如果多个控制器中都会抛出某个特定异常,可能需要在所有控制器中重复相同的@ExceptionHandler 方法,或者为了避免重复,创建一个基础控制器类,并继承这个类。
Spring 3.2 引入了一个新的解决方案:控制器通知。控制器通知是任意带有@ControllerAdvice注解的类,这个类会包含一个或多个如下类型的方法:

  • @ExceptionHandler 注解标注的方法
  • @InitBinder 标注的方法
  • @ModelAttribute 标注的方法。
    以上方法会应用到整个应用程序所有控制器中带有@RequestMapping 注解的方法上。
    @ControllerAdvice本身已经使用了 @Component 注解,会被自动扫描到。最实用的一个场景就是 统一异常处理,将所有@ExceptionHandler方法收集到一个类中
@ControllerAdvice
public class AppWideExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String hasException(){
        return  "has exception";
    }
}

4、跨重定向请求传递数据

当控制器返回的String 值以 redirect: 开头的话,那么这个 String 不是用来查找视图的,而是用来指导浏览器进行重定向操作。
正在发起重定向功能的方法如何发送数据给重定向的目标方法? 一般来讲,当一个处理器方法完成之后,该方法所指定的模型数据将会复制到请求中,并作为请求中的属性,请求会转发到视图上进行渲染。
但是如果控制器的结果是重定向的话,原始的请求就结束了,并且会发起一个新的 GET 请求。原始请求中所带有的模型数据也就随之消亡了。对于重定向模型并不能用来传递数据,可以通过其他方案传递数据:

  • 使用 URL 模板以路径变量和/或查询参数的形式传递数据;
  • 通过 flash 属性发送数据。

通过 URL模板进行重定向
可以直接通过拼接 String,当构建 URL 或 SQL 查询语句的时候,使用 String 连接是很危险的。
return "redirect:/user/" + user.getUseName;
除此之外,Spring 还提供了使用模板的方式定义重定向的 URL。例如:

//和模板占位符对应
model.addAttribute("userid",user.getUserId());
return "redirect:/user/{username}";

username 作为占位符填充到 URL 模板中,而不是直接拼接字符串,所以 username 中所有的不安全字符都会进行转义,这样会更安全。
如果添加 model.addAttribute("username",user.getUserName()); 但是 URL 中没有匹配的占位符,则自动以查询参数附加到重定向 URL 上。例如 username 为 cedar ,则重定向 url 路径为 /user/cedar?id=1
使用 flash 属性
如果希望传递整个对象,而非某些属性,可以将对象放到会话中,重定向后将其清理。Spring 采用这种方式提供了将数据发送为 flash 属性的功能,而无需自己管理这些数据。falsh 属性会一直携带这些数据直到下一次请求,然后自动消失。
Spring 通过 RedirectAttributesaddFlashAttribute() 方法将对象添加到模型中:

    @RequestMapping(value = "/userInfo" , method = RequestMethod.GET)
    public String  userInfo(String userId, RedirectAttributes model){
        User user = userService.getUserById(userId);
        model.addAttribute("userid",user.getId());
        //将 user 对象添加到模型中,不设置 key 则会根据值的类型自动判断
        model.addFlashAttribute("user",user);
        return  "redirect:/user/{userid}";
    }
    
    /**
     * 使用 flash 属性中的对象
     * @param username
     * @param model
     * @return
     */
    @RequestMapping(value = "/showProfile" , method = RequestMethod.GET)
    public  String showProfile(@PathVariable String userid, Model model){
        if (!model.containsAttribute("user")){
            model.addAttribute(userService.getUserById(userid));
        }
        return "profile";
    }

你可能感兴趣的:(Spring)