SpringMVC

一、SpringMVC概念及其准备工作

1、什么是MVC

MVC:Model/View/Controller 模型/视图/控制器,MVC是一种常见的设计层级关系。MVC描述的是一种结构,它用一种业务逻辑、数据与界面显示分离的方法来组织代码,最终目的是解耦,意思是你更改某一层代码,不会影响其他层的代码。

  • M:数据模型,提供要展示的数据,包含数据和行为,一般都分成Value Object(数据Dao)和服务层(行为Service),提供了模型数据查询和状态更新等功能。

  • V: View视图,进行模型展示, 产生html页面。

  • C: Controller控制器, 接收用户请求,委托给数据模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。

2、MVC模型处理数据的流程及各层职责

(1)MVC流程
在这里插入图片描述
1)用户发起请求
2)Servlet接受请求,并调用对应的业务逻辑方法
3)业务处理完成,返回数据给Servlet
4)Servet转向JSP,由JSP来渲染页面
5)响应给前端更新后的页面

(2)各层的职责:

Controller:控制器

  • 取得前端的表单数据
  • 调用业务逻辑
  • 转向指定的页面

Model:模型

  • 业务逻辑
  • 保存数据的状态

View:视图

  • 显示页面

(3)MVC框架要做哪些事情

  • 将url映射到java类或方法.
  • 封装用户提交的数据.
  • 处理请求–调用相关的业务处理–封装响应数据.
  • 将响应的数据渲染成. jsp / html 等表示层数据.

注意:

  • 常见的服务器端MVC框架有:Struts、SpringMVC、ASP.NET MVC、Zend Framework、JSF;
  • 常见前端MVC框架:vue、angularjs、react、backbone;
  • 由MVC演化出了另外一些模式如:MVP、MVVM
3、什么是SpringMVC

Spring MVC是Spring框架的一部分,是基于Java实现的MVC轻量级Web框架。

(1)Spring MVC的特点:

  • 轻量级,简单易学
  • 高效, 基于请求响应的MVC框架
  • 与Spring兼容性好,无缝结合
  • 约定优于配置
  • 功能强大:能够进行简单的junit测试、支持Restful风格、异常处理、本地化、国际化、数据验证、类型转换、拦截器等。
  • 简洁灵活

官方文档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.
html#spring-web

二、SpringMVC的流程:

Spring的web框架围绕DispatcherServlet 设计。DispatcherServlet的作用是将请求分发到不同的处理器。DispatcherServlet实际上是一个Servlet (它继承自HttpServlet 基类)

1、SpringMVC流程图

在这里插入图片描述

1)用户发送请求至前端控制器DispatcherServlet。DispatcherServlet用来映射所有请求/**

2)DispatcherServlet收到请求调用HandlerMapping处理器映射器

3)HandlerMapping处理器映射器根据请求url找到具体的处理器(Handler)(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器(Handler),处理器Handler的作用是根据url去寻找控制器。

5)Handler让具体的Controller执行(Controller,也叫后端控制器)。

6)Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView

7)HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9)ViewReslover解析后返回具体View.

10)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11)DispatcherServlet响应用户。

2、设计层次划分:

在这里插入图片描述

  • ServletRequestListener: 监听器
  • Filter: 过滤器
  • Dispatcher Servlet: 拦截所有的路径/**,并进行处理
  • HandlerInterceptor: 拦截器
  • ControllerAdvice: 处理异常及数据包装

三、SpringMVC应用

1、XML配置版

在这里插入图片描述
码云源码:SpringMVC配置版
(1)maven依赖
注意导入spring-webmvc依赖。

<!-- springmvc中包含spring核心包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.10.RELEASE</version>
    </dependency>


<!-- servlet 和jsp 的依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
    </dependency>

Maven可能存在资源过滤的问题:
有些xml,properties资源,maven可能加载不到,此时我们需要配置一下资源过滤。

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
                <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
                </includes>
            <filtering>false</filtering>
        </resource>
            <resource>
            <directory>src/main/resources</directory>
                <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
                </includes>
             <filtering>false</filtering>
        </resource>
    </resources>
</build>

(2)添加web依赖,配置web.xml ,注册DispatcherServlet

将所有请求都交给DispatcherServlet来处理。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--1.注册DispatcherServlet-->
<servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--关联一个springmvc的配置文件:-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别-1-->
        <load-on-startup>1</load-on-startup>
</servlet>

    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
     </servlet-mapping>

</web-app>

注意:

  • / 匹配所有的请求;(不包括.jsp),

  • /* 匹配所有的请求;(包括.jsp)

  • /*会匹配*.jsp会出现返回jsp视图时再次进入spring的DispatcherServlet
    类,导致找不到对应的controller,从而出现404错误。

  • DispatcherServlet关联一个springmvc的配置文件springmvc-servlet.xml

  • load-on-startup越小,该视图解析器的优先级越高

(3)编写SpringMVC的配置文件:springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--添加处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--添加处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!--添加视图解析器-->
    <bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--将Controller交给Spring容器-->
    <bean id="/hello" class="com.glp.controller.HelloController"/>

</beans>

注意:

  • 由于BeanNameUrlHandlerMapping映射器的存在,它会在Bean容器中寻找url的名字/hello

(4)编写controller层
编写Controller ,要么实现Controller接口,要么增加注解;

public class HelloController implements Controller {
   
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
     
        //ModelAndView 模型和视图
        ModelAndView mv = new ModelAndView();
        
        //封装对象,放在ModelAndView中。Model
        mv.addObject("msg","HelloSpringMVC!");
        
        //封装要跳转的视图,放在ModelAndView中
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;

    }
}

注意:

  • Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法。
  • 实现接口Controller定义控制器的缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;

(5)编写JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	${msg}
</body>
</html>

(6)配置Tomcat启动器

访问127.0.0.1:8080/hello出现404的可能情况:
在这里插入图片描述

2、注解版

springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器
为了方便配置,只需要开启注解驱动,就无需配置处理器映射器和处理器适配器了。

码云源码:SpringMVC注解版

(1)maben依赖和web.xml配置和上面的配置基本相同。

(2)springmvc-servlet.xml配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
    <!--扫描包:让指定包下的注解生效-->
    <context:component-scan base-package="com.glp.controller"/>

    <!--让springmvc不处理静态资源-->
    <mvc:default-servlet-handler />

    <!--自动注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter实例-->
    <mvc:annotation-driven />

    <!--添加视图解析器-->
    <bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

注意:

  • 所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个 目录下的文件,客户端不能直接访问。
  • <mvc:default-servlet-handler />静态资源过滤,配置完后.css,.js.html不用经过DispatcherServlet前置控制器的拦截。
  • <mvc:annotation-driven />自动注解驱动,加上之后可以采用@RequestMapping注解来完成映射关系,而不需要配置HandlerMapping和HandlerAdapter了。

(3)Controller

    @Controller
    public class HelloController {
    
        @RequestMapping("/hello")
        public String sayHello(Model model){
        
            //向模型中添加属性msg与值,可以在JSP页面中取出并渲染
            model.addAttribute("msg","hello,SpringMVC");
            
            //web-inf/jsp/hello.jsp
            return "hello";
        }
}
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • @RequestMapping用来映射请求路径
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。

(4)编写视图层
在WEB-INF/ jsp目录中创建hello.jsp ,视图可以直接取出并展示从Controller带回的信息。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	${msg}
</body>
</html>

注意:
两个不同的请求可以利用同一个视图,返回不同的数据,即视图和控制器之间实现了弱耦合。

四、RestFul风格

参考博客:RestFul风格的URL

五、结果跳转方式

1、ModelAndView

设置ModelAndView对象, 根据view的名称和视图解析器跳到指定的页面.

(1) 视图解析器

<!-- 视图解析器-->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
     id="internalResourceViewResolver">
   <!-- 前缀-->
   <property name="prefix" value="/WEB-INF/jsp/" />
   <!-- 后缀-->
   <property name="suffix" value=".jsp" />
</bean>

(2)controller

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(
   HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws Exception {

       ModelAndView mv = new ModelAndView();
       //添加模型数据
       mv.addObject("msg","ControllerTest1");
       //设置视图
       mv.setViewName("test");
       return mv;
  }
}

2、ServletAPI
  • 通过设置ServletAPI , 不需要视图解析器。
  • 通过HttpServletResponse进行输出,实现重定向以及转发。
@Controller
public class ResultGo {

	//利用Writer直接输出
   @RequestMapping("/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }
  
  //重定向
   @RequestMapping("/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
       rsp.sendRedirect("/index.jsp");
  }
  
  //转发
   @RequestMapping("/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp)
throws Exception {
       //转发
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }
}
3、SpringMVC

(1)通过SpringMVC来实现转发和重定向(没有视图解析器)

@Controller
public class ResultSpringMVC {

   //转发
   @RequestMapping("/t1")
   public String test1(){
   //没有视图解析器时,需要写全限定名
       return "WEB-INFO/jsp/index.jsp";
  }

	//转发
   @RequestMapping("/t2")
   public String test2(){
       return "forward:/index.jsp";
  }

   //重定向
   @RequestMapping("/t3")
   public String test3(){
       return "redirect:/index.jsp";
  }

(2)通过SpringMVC来实现转发和重定向(有视图解析器)
重定向, 不需要视图解析器, 本质就是重新请求一个新地方嘛, 所以注意路径问题.可以重定向到另外一个请求实现。

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/t1")
   public String test1(){
       //转发
       return "test";
  }
   @RequestMapping("/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";
  }
}

注意:

  • 当有视图解析器时,只需要提供视图的名称就可以了。
  • 如果使用POST方法,不可以直接转发一个html页面.

六、处理提交数据

1、提交的域名称和处理方法的参数名一致
http://localhost:8080/hello?name=glp

@RequestMapping("/hello")
public String hello(String name){}
2、提交的参数名和处理方法的参数名不一致
http://localhost:8080/hello?username=glp

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
}
3、提交的是一个对象

要求提交的参数名和对象的属性名一致时,Controller方法中的参数可以采用对象的形式。

public class User {
   private int id;
   private String name;
   private int age;
)

http://localhost:8080/mvc04/user?name=glp&id=1&age=15

@RequestMapping("/user")
public String user(User user){
   System.out.println(user);
   return "hello";
}

七、数据显示到前端

(1) 通过ModelAndView

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws Exception {

       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

(2)通过ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){

   model.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}

注意:

  • ModelMap封装要显示到视图中的数据,相当于req.setAttribute(“name”,name);

(3)通过Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){

   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}

八、乱码问题

SpringMVC给我们提供了一个过滤器, 可以在web.xml中配置.将所有请求包括.jsp等资源都经过过滤器进行处理。

<filter>
   <filter-name>encoding</filter-name>
   <filterclass>
org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
       <param-name>encoding</param-name>
       <param-value>utf-8</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>encoding</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

九、SpringMVC中的常用注解

(1)@RequestMapping

@RequestMapping(
value="/hello",
method=RequestMethod.POST)
  • @RequestMapping 可以在类、方法上使用。
  • 除了可以使用请求URL 映射请求外,还可以使用请求方法、请求参数及请求头映射请求。
  • @RequestMapping 的value、method、params 及heads
    分别表示请求URL、请求方法、请求参数及请求头的映射条件,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化

方法级别的注解变体(组合注解),如@GetMapping等价于@RequestMapping(method =RequestMethod.GET)

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

(2)@PathVariable
@PathVariable 映射URL 绑定的占位符,获取请求路径中的变量。

   @RequestMapping(value = "/{type}/login6")
    public Object login6(@PathVariable("type") String type){
            System.out.println(type);
            return type;
    }

(3)@RequestParam
@RequestParam 获取请求中的参数,在处理方法入参处使用@RequestParam 可以把请求参数传递给请求方法.
– value:参数名
– required:默认为true, 表示请求参数中必须包含对应
的参数,若不存在,将抛出异常

@RequestMapping("/world")
public String sayHello1(@RequestParam(value="username") String name){
    System.out.println(name);
    return "hello";
}

请求:http://localhost:8080/world?username=hellogggg
输出:hellogggg

(4)@RequestBody
@RequestBody: 请求数据类型为Content-Type=application/json时使用。

(5)@ResponseBody
@ResponseBody:返回Content-Type响应头为application/json。不带@ResponseBody时,需要返回字符串,表示静态资源路径。

(6)@CookieValue
@CookieValue 可让处理方法入参绑定某个Cookie 值

@RequestMapping("/world")
public String sayHello1(
@CookieValue(value="sessionid",required = false) String session){}

(7)使用ServletAPI入参
直接注入HttpServletRequestHttpServletResponse.使用Servlet API对象作为入参:一些servlet操作(获取session,设置头等)可以直接使用request 和 response来完成。

@RequestMapping(value="method")
public void method5(HttpServletRequest request,HttpServletResponse response){
   String userName = “hello”;
   response.addCookie(new Cookie("userName",userName));
}

(8)使用POJO 对象绑定请求参数值
Spring MVC 会按请求参数名和POJO 属性名进行自动匹配,自动为该对象填充属性值。

http://localhost:8080/mvc04/user?name=glp&id=1&age=15

@RequestMapping("/user")
public String user(User user){
   System.out.println(user);
   return "hello";
}

十、SpringMVC中的拦截器、异常处理、数据封装

1、拦截器

拦截器类似于Servlet开发中的过滤器Filter。拦截器是基于AOP思想的具体应用,属于SpringMVC框架。

自定义的拦截器必须实现HandlerInterceptor接口

  • preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
  • postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet
    向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
  • afterCompletion():这个方法在DispatcherServlet 完全处理完请
    求后被调用,可以在该方法中进行一些资源清理的操作。

(1)在配置类中添加拦截器
实现WebMvcConfigurer接口,重写里面的addInterceptors方法。

@Configuration
public class MyConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
        		//所有匹配的路径,都会进入拦截器
        		//添加路径
                .addPathPatterns("/1/**")
                //**代表所有子路径,*只匹配一级路径
                .excludePathPatterns("/1/login5");//排除路径
    }
}

注意:
WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些拦截器、静态资源、视图解析器、页面跳转。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口;
参考博客;WebMvcConfigurer详解

(2)使用xml配置拦截器配置:

<!--关于拦截器的配置-->
<mvc:interceptors>
   <mvc:interceptor>
       <!--/** 包括路径及其子路径-->
       <!--/admin/* 拦截的是/admin/add等等这种, /admin/add/user不会被拦截-
->
       <!--/admin/** 拦截的是/admin/下的所有-->
       <mvc:mapping path="/**"/>
       <!--bean配置的就是拦截器-->
       <bean class="com.glp.config.LoginInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

(3)拦截器
截器需要实现HandlerInterceptor接口

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
		//有session
        if(session != null){//有登录
           return true;
        }
        response.setStatus(HttpStatus.UNAUTHORIZED.value());//401
        response.sendRedirect("/login.html");
        //登录不成功,直接跳到登录页面
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

注意:

  • HttpSession session = request.getSession(false);设置false,如果没有session则不创建。
  • response.setStatus(HttpStatus.FORBIDDEN.value());通过枚举类设置状态码。
2、异常处理

Spring MVC 通过HandlerExceptionResolver 处理程序的异常,包括Handler 映射、数据绑定以及目标方法执行时发生的异常。

  • 如果在当前handler中找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice注解的类中的@ExceptionHandler 方法来处理异常
  • 加入@ControllerAdvice注解会获取出现的异常,具体的拦截要通过方法来完成。需要在方法上加入@ExceptionHandler(Exception.class)注解,Exception.class为拦截所有的异常。
  • 针对普通请求需要返回一个页面,针对json请求需要返回json数据。
@ControllerAdvice
public class ExceptionAdvisor {

  ==================返回json数据的错误信息========================
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object handle(Exception e){//Controller方法抛出的异常,会注入到请求参数
        Map<String, String> map = new HashMap<>();
        map.put("error", e.getMessage());
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        map.put("stackTrace", sw.toString());
        return map;
    }
====================返回静态资源页面==========================
    @ExceptionHandler(Exception.class)
    public String handle(HttpServletResponse response){//Controller方法抛出的异常,会注入到请求参数
        try {
            response.sendRedirect("/error.html");//通过post请求过来不能直接转发为/error.html,但可以重定向
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

注意:

  • @ExceptionHandler(Exception.class)中可以标注特定的异常类,用来捕获特性的异常,如@ExceptionHandler(NullPointerException.class)
  • @ResponseStatus(reason="越界",value="HttpStatus.NOT_FOUND")定制返回的状态码

判断是否为ajax请求工具:
如果是ajax请求则返回json数据;如果不是,则返回普通页面。

package com.glp.util;

import javax.servlet.http.HttpServletRequest;

public class CrowdUtil {
	
	/**
	 * 判断当前请求是否为Ajax请求
	 * @param request 请求对象
	 * @return
	 * 		true:当前请求是Ajax请求
	 * 		false:当前请求不是Ajax请求
	 */
	public static boolean judgeRequestType(HttpServletRequest request) {
		
		// 1.获取请求消息头
		String acceptHeader = request.getHeader("Accept");
		String xRequestHeader = request.getHeader("X-Requested-With");
		
		// 2.判断
		return (acceptHeader != null && acceptHeader.contains("application/json"))
				
				||
				
				(xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"));
	}

}
3、数据包装

(1)@ControllerAdvice统一数据包装
@ControllerAdvice除了拦截异常,还可以拦截请求方法,也就是说数据包装可以和异常处理写到一起,也可以把他们分开。

要实现ResponseBodyAdvice接口。通过获取请求方法对象,判断Controller方法上是否有@ResponseBody注解,进而使用beforeBodyWrite方法来对返回数据进行包装。

@ControllerAdvice
public class ResponseAdvisor implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //返回true,表示响应数据时,先执行beforeBodyWrite
        //获取请求方法对象,判断Controller方法上是否有@ResponseBody
        return returnType.getMethod().getAnnotation(ResponseBody.class) != null;
    }

    //body为请求方法执行完以后,返回的对象
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Response resp = new Response();
        resp.setSuccess(true);
        resp.setData(body);
        return resp;
    }
}

注意:
supports方法中返回true,表示响应数据时,先执行beforeBodyWrite方法。
body为请求方法执行完以后,返回的对象。

Response.java
通过自定义Response.java来对数据body进行进一步的包装。

package com.glp.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Response {

    private boolean success;//操作是否成功
    private String code;//错误码
    private String message;//错误信息
    private String stackTrack;//堆栈信息
    private Object data;//数据
}

(2)统一整个项目中Ajax请求返回的结果

package com.glp.util;

/**
 * 统一整个项目中Ajax请求返回的结果(未来也可以用于分布式架构各个模块间调用时返回统一类型)
 * @author glp
 *
 * @param <T>
 */
public class ResultEntity<T> {
	
	public static final String SUCCESS = "SUCCESS";
	public static final String FAILED = "FAILED";
	
	// 用来封装当前请求处理的结果是成功还是失败
	private String result;
	
	// 请求处理失败时返回的错误消息
	private String message;
	
	// 要返回的数据
	private T data;
	
	/**
	 * 请求处理成功且不需要返回数据时使用的工具方法
	 * @return
	 */
	public static <Type> ResultEntity<Type> successWithoutData() {
		return new ResultEntity<Type>(SUCCESS, null, null);
	}
	
	/**
	 * 请求处理成功且需要返回数据时使用的工具方法
	 * @param data 要返回的数据
	 * @return
	 */
	public static <Type> ResultEntity<Type> successWithData(Type data) {
		return new ResultEntity<Type>(SUCCESS, null, data);
	}
	
	/**
	 * 请求处理失败后使用的工具方法
	 * @param message 失败的错误消息
	 * @return
	 */
	public static <Type> ResultEntity<Type> failed(String message) {
		return new ResultEntity<Type>(FAILED, message, null);
	}
	
	public ResultEntity() {
		
	}

	public ResultEntity(String result, String message, T data) {
		super();
		this.result = result;
		this.message = message;
		this.data = data;
	}

	@Override
	public String toString() {
		return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
	}

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

}

使用:
根据实际情况,调用ResultEntity中的方法,比如失败、成功、成功带数据、成功不带数据等。

  @ResponseBody
    @RequestMapping(value = "/send/obj.json")
    public ResultEntity<Student> testReceiveObj(@RequestBody Student student, HttpServletRequest request) {
    
        ResultEntity<Student> resultEntity = ResultEntity.successWithData(student);
        return resultEntity;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值