springMVC_2(restful、异常处理、静态资源、文件上传、拦截器、跨域)

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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值