springMVC

    科技2025-04-18  9

    文章目录

    rest风格接口SpringMVC异常处理静态资源处理文件上传下载拦截器跨域请求方法1:@CrossOrigin注解方法2:springMVC的全局配置方法3:CrosFilter过滤器方法4:自定义跨域处理过滤器(推荐)方法5:springboot的解决方式

    rest风格接口

    restfurl:描述性状态转移。用来做接口。

    通过url来定义资源,通过method(get、post、put、delete)来描述动作。

    涉及到的两个注解

    @RestController(不常用),定义接口,表示本类中所有的方法都是异步,不用写ResponseBody,不用写ResponseEntity

    @PathValiable:注解处理器方法的形参,用来获取url中变量值。

    注意点:在put与delete请求中,请求参数如果是key=value&key=value,后端收不到数据?

    tomcat默认只对get和post请求的key=value&key=value解析,保存内部的map,我们可以通过getParameter来获取数据。

    解决方法:在web.xml配置过滤器FormContentFilter,对put和delete请求支持。

    @Controller @RequestMapping("rest") public class RestUrlHandler { @GetMapping("{sid}") // 查询 http://localhost:8080/0803/rest/12 @ResponseBody public ResponseBean getStuById(@PathVariable("sid") String sid){ SysStudent stu = new SysStudent(); stu.setSid(sid); stu.setSname("千珏"); stu.setSpass("1234"); return new ResponseBean(StatusEnum.SUCCESS,stu); } @PostMapping // 新增 http://localhost:8080/0803/rest public ResponseEntity addStu(@RequestBody SysStudent sysStudent){ System.out.println(sysStudent); return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS)); } @PutMapping // 修改 http://localhost:8080/0803/rest public ResponseEntity editStu(@RequestBody SysStudent sysStudent){ System.out.println(sysStudent); return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS)); } @DeleteMapping("{sid}") // 删除 http://localhost:8080/0803/rest/12 public ResponseEntity deleteStuById(@PathVariable("sid") String sid){ System.out.println(sid); return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS)); } }

    SpringMVC异常处理

    原生servlet玩法:在web.xml配置错误代码,错误页面; <error-page> <error-code>404</error-code> <location>/404.html</location> </error-page>

    局部异常处理:对某个处理器类中的异常处理。通过@ExceptionHandler注解

    @ExceptionHandler可以转页面,可以返回json数据。缺点:只能对一个类中的异常生效。 @Controller @RequestMapping("exception") public class ExceptionTestHandler { @ExceptionHandler(Exception.class) @ResponseBody public Map<String,Object> doException(Exception e){ Map<String,Object> map = new HashMap<>(); map.put("time",System.currentTimeMillis()); map.put("info",e.getMessage()); return map; } @DeleteMapping("{sid}") // 删除 public ResponseEntity deleteStuById(@PathVariable("sid") Integer sid){ // int i = 1/0; // 算术异常 if (sid == 12){ throw new RuntimeException(StatusEnum.ERROR.getMsg()); } System.out.println(sid); return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS)); } }

    全局异常处理:对所有的处理器类中的异常处理。实现HandlerExceptionResolver接口。

    针对的同步请求,返回错误页面,可以自定义错误信息。

    缺点:对于前后端分离项目,不能返回数据。

    @Component public class GlobExceptionHandler implements HandlerExceptionResolver { //返回ModelAndView对象.处理同步请求。 @Override public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp, Object o, Exception e) { ModelAndView mv = new ModelAndView(); mv.setViewName("error"); mv.addObject("time",System.currentTimeMillis()); mv.addObject("info",e.getMessage()); HandlerMethod hm = (HandlerMethod) o; Method method = hm.getMethod(); String name = method.getDeclaringClass().getName(); mv.addObject("clzName",name); return mv; } }

    全局统一异常处理:通过@ControllerAdvice与@ExceptionHandler两者结合。

    一个状态枚举类,两个响应实体类(StatusBean,ResponseBean)

    正常返回,返回ResponseBean

    异常情况,返回StatusBean,全部进入一个统一的全局异常处理类。

    @Component // 注入容器 @ControllerAdvice // controller增强注解。做异常的统一处理。 public class GlobExceptionHandler2 { @ExceptionHandler(Exception.class) public ResponseEntity doException(Exception e){ return ResponseEntity.ok(new StatusBean(StatusEnum.OPS_ERROR)); } @ExceptionHandler(MyException.class) public ResponseEntity doException(MyException e){ return ResponseEntity.ok(new StatusBean(e.getStatusEnum())); } }

    静态资源处理

    静态资源包括:css、js、jpg、png、音频、视频、html等。解决方式1: <!--所有的静态资源都走DefaultServlet,依赖应用服务器名字叫default的serlvet--> <mvc:default-servlet-handler></mvc:default-servlet-handler> 解决方式2: <!--对静态资源做映射,映射到一个springmvc提供的公共的静态资源处理器--> <mvc:resources location="/static/" mapping="/static/**"></mvc:resources> <mvc:resources location="/error/" mapping="/error/**"></mvc:resources>

    文件上传下载

    SpringMVC对apache的commons-fileupload组件进行封装,避免了繁琐的request解析操作。

    操作流程:

    导入jar包:fileUpload、io配置bean <!-- id:必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="50000000"></property><!-- 最大文件大小 --> <property name="maxInMemorySize" value="10000000"></property><!-- 临时文件域 --> <property name="defaultEncoding" value="UTF-8"></property><!-- 中文文件名 --> <property name="uploadTempDir" value="/upload/tmp"></property><!-- 临时文件存储路径 --> </bean> 在handler类中添加upload()方法,参数列表添加MultipartFile类型形参接收上传的文件。

    多文件上传,upload方法MultipartFile形参改为数组,或多个MultipartFile类型的形参。

    @Controller @RequestMapping("file") public class FileHandler { // 文件上传 @PostMapping("upload") public ResponseEntity doUpload(MultipartFile file, HttpServletRequest req) throws IOException { if (file == null) { System.out.println("无文件"); return ResponseEntity.ok("无文件"); } InputStream inputStream = file.getInputStream(); // 大文件上传 byte[] bytes = file.getBytes();// 适合小文件,10M以下 String filename = file.getOriginalFilename(); // 文件真实名 long size = file.getSize();// 文件大小 // 上传文件至服务器 String realPath = req.getServletContext().getRealPath("/"); String savePath = "/upload/" + filename; System.out.println(realPath + savePath); File target = new File(realPath + savePath); FileUtils.writeByteArrayToFile(target, bytes); // 保存文件 // 把文件的保存路径返回给客户端 return ResponseEntity.ok(new ResponseBean(StatusEnum.SUCCESS, savePath)); // /upload/dui.png } //前端如何调用下载接口:<a href="/download?filepath=/upload/aaa.png"> <button click="location.href=/download?filepath=xxxxx"></button> @GetMapping("/download") public ResponseEntity doDownLoad(String filepath, HttpServletRequest req) throws IOException { System.out.println("文件下载!"); // /upload/dui.png String realPath = req.getServletContext().getRealPath(filepath); File file = new File(realPath); String name = file.getName(); byte[] bytes = FileUtils.readFileToByteArray(file); if (bytes != null) { //指定响应头,传字节数组 HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name, "UTF-8")); return new ResponseEntity(bytes, headers, HttpStatus.OK); } else { throw new NullPointerException(StatusEnum.ERROR.getMsg()); } } }

    拦截器

    interceptor拦截器,是控制层框架中的一个概念,与Filter概念一样。

    url请求,处理器映射器找到一个处理器方法对象以及一系列的拦截器对象(preHandle,postHandle,afterCompletion)。在执行处理器方法的前,后,异常处执行拦截器对应的方法。

    6.1 从HandlerInterceptor派生拦截器类。

    6.2 配置拦截器(拦截范围,忽略范围)

    <mvc:interceptors> <mvc:interceptor> <!--配置拦截路径--> <mvc:mapping path="/**"/> <!--忽略路径--> <mvc:exclude-mapping path="/user5/login"></mvc:exclude-mapping> <mvc:exclude-mapping path="/static/**"></mvc:exclude-mapping> <!--拦截器bean对象--> <bean class="com.javasm.sys.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object login_user = session.getAttribute("LOGIN_USER"); if(login_user!=null){ return true; }else{ //未登陆,要做什么. // 如果是同步请求,可以转登陆页面 // 如果是异步请求,则返回数据。 throw new MyException(StatusEnum.NO_LOGIN); } } // @Override // public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // //处理器方法执行完毕,并正常返回,执行这里 // System.out.println("后拦截"); // } // // @Override // public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // //处理器方法执行完毕,不论有没有异常,都执行最终拦截 // System.out.println("最终拦截"); // } }

    跨域请求

    跨域是指浏览器不允许当前页面的所在的源去请求另一个源的数据。源指协议,端口,域名。只要这个3个中有一个不同就是跨域。

    方法1:@CrossOrigin注解

    该注解添加到在处理器方法或类上。缺点:只能够针对单个类处理。

    方法2:springMVC的全局配置

    <mvc:cors> <mvc:mapping path="/**" allowed-origins="*" allowed-headers="*" allowed-methods="*" allow-credentials="true"/> </mvc:cors> // 前端axios需要开启跨域支持 axios.defaults.baseURL = 'http://localhost:8080'; axios.defaults.withCredentials=true;//跨域支持 缺点:方法1与方法2在正常情况下都能够正常处理跨域请求,一旦项目中引入拦截器,则跨域处理失效,因为springmvc的cors配置也是基于拦截器实现的,并且默认作为最后一个拦截器处理,这样会导致在跨域拦截器之前执行了业务拦截器。比如:引入登录拦截器,A请求被登录拦截,则不向下执行跨域拦截器处理,该请求则不支持跨域,客户端收到错误信息。(springMVC高版本貌似已解决此问题)

    方法3:CrosFilter过滤器

    <!-- 这个配置需要放到Spring的配置文件中,不能放到SpringMVC的配置文件,因为SpringMVC的加载是基于Servlet,它是晚于Filter的 --> <bean id="corsFilter" class="org.springframework.web.filter.CorsFilter"> <constructor-arg name="configSource"> <bean class="org.springframework.web.cors.UrlBasedCorsConfigurationSource"> <property name="corsConfigurations"> <map> <entry key="/**"> <bean class="org.springframework.web.cors.CorsConfiguration"> <property name="allowCredentials" value="true"/> <property name="allowedMethods"> <list> <value>GET</value> <value>POST</value> <value>HEAD</value> <value>PUT</value> <value>Delete</value> <value>OPTIONS</value> </list> </property> <property name="allowedHeaders" value="*"/> <property name="allowedOrigins" value="*"/> </bean> </entry> </map> </property> </bean> </constructor-arg> </bean> <!-- web.xml 由于CorsFilter跟通常的Filter不一样,Spring对其做了很多改造,所以加载的方式要使用DelegatingFilterProxy,通过Spring的方式把它放到容器中 --> <filter> <filter-name>myCorsFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>corsFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>myCorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

    方法4:自定义跨域处理过滤器(推荐)

    public class MyCrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // 预检请求,请求头Origin是客户端地址,要求跨域头不能是* String origin = httpRequest.getHeader("Origin"); if (origin == null) { httpResponse.addHeader("Access-Control-Allow-Origin", "*"); } else { httpResponse.addHeader("Access-Control-Allow-Origin", origin); } httpResponse.addHeader("Access-Control-Allow-Headers", "Origin, x-requested-with, Content-Type, Accept,X-Cookie,token"); //httpResponse.addHeader("Access-Control-Expose-Headers", "token"); httpResponse.addHeader("Access-Control-Allow-Credentials", "true"); httpResponse.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE"); //预检请求,直接通过。 if (httpRequest.getMethod().equals("OPTIONS")) { httpResponse.setStatus(HttpServletResponse.SC_OK); return; } chain.doFilter(request, response); } catch (Exception e) { throw e; } } @Override public void destroy() { } } web.xml配置 <!--跨域拦截器的配置--> <filter> <filter-name>myCrosFilter</filter-name> <filter-class>com.javasm.common.filter.MyCrosFilter</filter-class> </filter> <filter-mapping> <filter-name>myCrosFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

    方法5:springboot的解决方式

    @Configuration public class MyConfiguration { //springmvc中 没有FilterRegistrationBean这个类 @Bean public FilterRegistrationBean这个类 corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("http://domain1.com"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; } }
    Processed: 0.008, SQL: 8