SpringMVC(二) 页面转发、重定向、异步、Restful、异常处理、拦截器

SpringMVC(二) 页面转发、重定向、异步、Restful、异常处理、拦截器

页面跳转之转发

方式一:简单方式

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/forward1">普通转发</a>
  • 后台
//页面转发方式1
@RequestMapping("/respServlet/forward1")
public String forward1() {
    return "success";
}

方式二:使用forward转发

在返回中使用forward:url表示使用forward转发

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/forward2">forward转发</a>
  • 后台
@RequestMapping("/forward2")
public String forward2() {
    return "forward:/WEB-INF/fail.jsp";
}

方式三:使用Servlet原生API

通过SpringMVC前端控制器传递回来的参数可以接收到HttpServletRequest HttpServletResponse对象,使用ServletAPI可以进行转发

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/forward3">Servlet原生API</a>
  • 后台
@RequestMapping("/forward3")
public void forward3(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/fail.jsp")
            .forward(request, response);
}

转发携带数据

进行页面转发时,通常我们都会携带数据,SpringMVC中我们

  • 可以使用HttpServletRequest request
@RequestMapping("/forward4")
public String forward4(HttpServletRequest request) {
    //使用Request传值
    request.setAttribute("pageValue", "zs");
    return "success";
}
  • 还可以使用Model 对象
@RequestMapping("/forward5")
public String forward5(Model model) {
    //传递数据
    model.addAttribute("pageValue", "ss");
    return "success";
}
  • 还可以使用ModelAndView对象
@RequestMapping("/forward6")
public ModelAndView forward6() {
    //构造ModelAndView对象
    ModelAndView modelAndView = new ModelAndView();
    //设置数据
    modelAndView.addObject("pageValue", "fromModelAndView");
    //设置逻辑视图
    modelAndView.setViewName("success");
    return modelAndView;
}

页面跳转之重定向

使用redirect重定向

在Controller的返回中使用 redirect:url代表页面重定向

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect1">redirect重定向</a>
  • 后台
@RequestMapping("/redirect1")
public String redirect1() {
    return "redirect:/success.jsp";
}

使用Servlet原生API

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect2">使用Servlet原生API</a>
  • 后台
@RequestMapping("/redirect2")
public void redirect2(HttpServletRequest request,
                      HttpServletResponse response) throws IOException {
    response.sendRedirect(request.getContextPath() + "/success.jsp");
}

使用重定向的方式进入外界无法访问的WEB-INF目录下

我们知道在javaWeb项目中,WEB-INF目录下的资源是无法通过外界访问的。但是可以通过服务器内部的请求转发访问到,现在我们想使用页面重定向技术进行访问WEB-INF目录下的资源。我们可以经过一次转发来达到通过重定向技术来访问WEB-INF目录下的资源

  • 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect3">使用重定向-转发来访问WEB-INF下的资源</a>
  • 后台

    @RequestMapping("/redirect3")
    public String redirect3() {
        return "redirect:/respServlet/redirforwardtoSuccess";
    }
    //转发方法
    @RequestMapping("/redirforwardtoSuccess")
    public String redirectForward() {
        //转发到指定资源中
    
        return "forward:/WEB-INF/fail.jsp";
    }
    

释放静态资源

当有静态资源需要加载时,比如js css 等,如果在url-pattern中配置的是/,代表除了.jsp请求不拦截,其他的所有请求都会拦截,包括一些静态文件,而拦截之后,SpringMVC又找不到对应的处理器来处理,因此我们需要释放静态资源,来让静态资源能够被正常加载

方式一

<!--
    释放资源:
    mapping:代表匹配的路径规则
    location:代表匹配的路径规则的静态资源的位置
-->
    <mvc:resources mapping="/js/*" location="/js/"/>

方式二

指定处理器映射器寻找不到对应的处理方法时,暂时不要报错,而是直接丢给外部的默认Servlet,也就是tomcat处理

<!--    指定错误交给外部的处理器处理-->
    <mvc:default-servlet-handler/>

Ajax+json实现异步交互

在SpringMVC中,前端发送Ajax异步请求,后端返回json响应。这个功能主要是通过两个注解@RequestBody和@ResponseBody实现的

SpringMVC默认使用MappingJackson2HttpMessageConverter对json数据进行转换,需要加入jackson的包

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

@RequestBody接收ajax请求

@RequestBody用于接收前端传递的请求体重的json数据,并可以自动转换封装进指定的对象中

  • 页面
    • 其中contentType代表请求参数类型
    • dataType代表响应数据类型
<button id="btn_ajax">提交ajax请求</button>
<script src="/js/jquery-3.3.1.js"></script>
    <script>
         $(function () {
            $("#btn_ajax").click(function () {
                $.ajax({
                    type: "POST",
                    url: "${pageContext.request.contextPath}/respServlet/ajaxReq",
                    contentType: "application/json",
                    dataType: "json",
                    data: '[{"name":"张三","age":19},{"name":"李四","age":19}]',
                    success: function (data) {
                        console.log(data);
                        alert(data);
                    }
                });
            });
        });
    </script>
  • 封装实体类
public class User {
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
  • 后台
 @ResponseBody
    @RequestMapping("/ajaxReq")
    public List<User> ajaxReq(@RequestBody List<User> users) {
        System.out.println("");
        users.stream().forEach(user -> System.out.println(user));
        return users;
    }

@ResponseBody返回Json数据

@responseBody用于将controller方法返回的对象通过转换器转换为指定的格式(通常为json),

@ResponseBody
@RequestMapping("/ajaxReqStr")
public String ajaxReqStr(@RequestBody User user) {
    System.out.println(user);
    return "ok";
}

Spring还提供了一个@RestController 表示@Controller+@ResponseBody

Restful风格

定义

REST是一种软件架构风格,其强调HTTP应当以资源为中心

REST规范了HTTP请求动作,使用四个词语分别表示对资源的CRUD操作

也就是说Restful风格是通过方法的method类型来区分各种不同的业务

原来 Restful
保存 /saveUser post /usr
修改 /updateUser?id=1 Put /user/1
删除 /deleteUser?id=1 delete /user/1
查询所有 /findAllUser get /user
查询一个 /findUserById?id=1 get /user/1

保存

  • 页面
$("#save").click(function () {
    $.ajax({
        type: "POST",
        url: "${pageContext.request.contextPath}/user",
        contentType: "application/json",
        dataType: "text",
        data: '{"name":"user1","age":11}',
        success: function (data) {
            alert(data);
        }
    });
})
  • 后台

注解@PostMapping 相当于@RequestMapping(value="/user" method="POST")

@PostMapping("/user") //@PostMapping 相当于    @RequestMapping(value = "/user",method = RequestMethod.POST)
@ResponseBody
public String saveUser(@RequestBody User user) {
    System.out.println("收到的参数" + user);
    return "ok";
}

查询

get请求没有请求体,所以参数需要在请求地址中传递

  • 页面
$("#find").click(function () {
    $.ajax({
        type: "GET",
        url: "${pageContext.request.contextPath}/user/name/user1/age/11",
        contentType: "application/json",
        dataType: "text",
        success: function (data) {
            alert(data);
        }

    });
});
  • 后台
@GetMapping("/user/name/{name}/age/{age}")
@ResponseBody
public String findUser(@PathVariable String name,
                       @PathVariable String age) {
    System.out.println("接收到的参数" + name + age);
    return "ok";
}

异常处理机制

Spring框架,我们通常将异常抛到框架中,然后指定Spring的异常处理器来统一处理异常

方式一:自定义异常处理器

可以实现HandlerExceptionResolver接口,实现接口方法来自定义异常处理器

public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        return new ModelAndView("error", "err", e.getMessage());
    }
}
  • 加入扫描注解
<context:component-scan base-package="com.itheima.exceptionhandler"/>

方式二:@ControllerAdvice

使用注解@ControllerAdvice注解 @ExceptionHandler(Exception.class)

@ControllerAdvice
public class MyExceptionHandler2 implements HandlerExceptionResolver {
    //声明处理哪些异常
    @ExceptionHandler(Exception.class)
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        return new ModelAndView("WEB-INF/error", "err", e.getMessage());
    }
}

拦截器

什么是拦截器

拦截器是Spring提供的一种技术,类似于Servlet的Filter

拦截器的特点

  • 会在请求进入controller之前
  • 离开controller之后
  • 页面渲染完毕之后

自定义拦截器

自定义一个类实现HandlerInterceptor接口

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入控制器之前");
        //代表是否放心  true 代表放行 false代表不放心
        return false;
    }

    @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("页面渲染完成之后");
    }
}
  • Springmvc中配置拦截规则
  <!--    配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            要拦截的路径规则 /**代表拦截所有-->
            <mvc:mapping path="/**"/>
<!--            不拦截的路径规则-->
            <mvc:exclude-mapping path="/login"/>
<!--            指定拦截器-->
            <bean class="com.itheima.interceptor.MyInterceptor"/>

        </mvc:interceptor>
    </mvc:interceptors>

自定义拦截器链

开发中拦截器可以单独使用,也可以同时使用多个拦截器形成一条拦截器链

开发步骤和单个拦截器是一样的,只不过注册多个拦截器的时候,注册的顺序代表拦截器的执行顺序

  • 定义第二个拦截器
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:mapping path="/register"/>
    <bean class="com.itheima.interceptor.MyInterceptor2"/>
</mvc:interceptor>

拦截器和过滤器的区别

  • 拦截器属于SpringMVC框架,只有使用了SpringMVC框架才能使用拦截器,Servlet属于JavawebAPI 只要是JavaWeb工程都可以使用
  • 过滤器在url-pattern中配置了/之后,可以对所有资源拦截,拦截器配置了/ 之后 只会拦截访问的控制器方法,不会拦截静态资源

案例 使用拦截器完成用户访问的拦截

req:

  • 用户访问一个页面index.jsp
  • 如果用户已经登录,可直接进入index.jsp
  • 如果用户没有登录,跳转到登录页面
  • 页面
  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="text" name="password"><br/>
    <input type="submit" value="提交">
</form>
</body>
</html>
  • index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
登录完成 ${username}
</body>
</html>
  • 后台Controller
@Controller
public class UserController {
    @RequestMapping("/login")
    public String login(String username, String password, HttpSession session) {
        if (username != null && !username.equals("")) {
            if (username.equals("admin")) {
                //登录成功 存入session
                session.setAttribute("loginName", username);
                return "forward:/index";
            }
        }
        return "redirect:/login.jsp";
    }


    @RequestMapping("/index")
    public String toIndex() {
        return "WEB-INF/index";
    }
}
  • 拦截器
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断是否已经存储
        String loginName = (String) request.getSession().getAttribute("loginName");
        if (loginName != null && !"".equals(loginName)) {
            //不为空 放行
            return true;
        } else {
            //为空 重定向到登录页面
            response.sendRedirect(request.getContextPath() + "/login.jsp");
            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 {

    }
}
  • 拦截器配置
<!--    配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/login"/>
        <bean class="com.itheima.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容