SpringBoot02-SpringMVC入门

五、SpringMVC

SpringMVC其实是 spring 框架中关于 web,webmvc 开发的一个技术

spring 核心 IOC ,AOP ,Web开发

5.1 MVC

m: model

v: view

c: controller

没有 mvc 时,全部一个类做完,不符合单一职责图片

有了 mvc 架构设计图片

MVC 架构:根据不同的事情由不同的类去处理,内部单一职责

  • Model: 模型类,例如封装数据的实体类,业务模型 (Service), 数据层 (Dao)
  • View: 视图,展示数据的.HTML,JSP
  • Controller: 控制器,控制整个流程走向。决定是否能接收请求,调用哪个业务,跳转哪个页面,Servlet

MVC 框架 (SpringMVC 框架) 特点

  • 封装了 Servlet
  • 接收请求方便
  • 接收请求数据方便
  • 响应数据方便

5.2 搭建环境

  • 创建 springboot 项目
  • 导入 spring-boot-starter-web 这个依赖,即可开始开发 webmvc 的代码

5.2 接收请求【重点】

即前后如何通过路径匹配想要在 Controller 层接收请求,只需要

  • 创建一个类,类上加 @Controller 注解
  • 设计一个方法
  • 给方法添加请求映射【路径】的注解
    • @RequestMapping 或
    • @GetMapping 或 @PostMapping 等
java 复制代码
package com.qf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
 * 演示接收请求
 */
@Controller
@RequestMapping("/test")
public class Demo1Controller {
    /**
     * 以前servlet接收请求,需要创建一个类,继承HttpServlet,才能接收一个请求
     * 即再有一个请求,就需要再创建一个类,继承HttpServlet,才能接收
     * ...麻烦!!!
     *
     * 现在springmvc接收请求,只需要在一个类中,添加一个方法,即可接收一个请求
     */
    /**
     * 方法上@RequestMapping 匹配前端发出的请求路径,
     *   默认可以get和post请求
     * 也可以在类上添加@RequestMapping,
     * 类上的@RequestMapping 是前缀和方法上的@RequestMapping 拼接的
     *
     * /test/t1
     */
    @RequestMapping("/t1")
    public String test1(){
        System.out.println("test1方法执行了");
        return "/ok.html";
    }
    /**
     * 注解@RequestMapping内部是可以指定接收哪个请求
     * @RequestMapping(value="/t2",method = RequestMethod.GET)
     * 这样,就只能接收get请求,不能接收post请求
     * 如果发送post请求,就会报错405
     */
    @RequestMapping(value="/t2",method = RequestMethod.GET)
    public String test2(){
        System.out.println("test2方法执行了");
        return "/ok.html";
    }
    /**
     * @RequestMapping(value="/t2",method = RequestMethod.GET)
     * 一般会简化注解使用@GetMapping("/t2")
     */
    @GetMapping("/t3")
    public String test3(){
        System.out.println("test3方法执行了");
        return "/ok.html";
    }
    @PostMapping("/t4")
    public String test4(){
        System.out.println("test4方法执行了");
        return "/ok.html";
    }
}

思考如何发出请求 测试?

  • 浏览器地址栏,手输入,get 方式
  • a 标签,默认发出的 get 请求
  • form 表单,指定 method 发出 get 或 post 请求
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  <body>
    <h1>hello word!!!</h1>
    <!-- href是请求后端地址-->
    a标签发送get请求: <a href="/test/t1">/test1</a> <br>
    表单发送请求1,post方式,路径/test/t1
    <form action="/test/t1" method="post">
      <input type="submit" value="发送post请求">
    </form>
    表单发送请求2,get方式,路径/test/t1
    <form action="/test/t1" method="get">
      <input type="submit" value="发送get请求">
    </form>
    <hr>
    表单发送请求3,post方式,路径/test/t2
    <form action="/test/t2" method="post">
      <input type="submit" value="发送post请求">
    </form>
    表单发送请求4,get方式,路径/test/t2
    <form action="/test/t2" method="get">
      <input type="submit" value="发送get请求">
    </form>
    <hr>
    表单发送请求5,get方式,路径/test/t3
    <form action="/test/t3" method="get">
      <input type="submit" value="发送get请求">
    </form>
    表单发送请求6,post方式,路径/test/t4
    <form action="/test/t4" method="post">
      <input type="submit" value="发送post请求">
    </form>
  </body>
</html>

5.3 参数绑定 (接收请求数据) 【重点】

所谓参数绑定,就是前端发请求中的数据,可以直接在 Controller 的方法参数中接收。即前端请求数据和后端方法参数绑定.

5.3.1 简单类型参数绑定 [重点]

简单类型指,常用的几种类型:基本类型 + String+Date前端页面

html 复制代码
<h3>测试-接收简单类型数据</h3>
<!--a标签发送get请求,拼接数据,记住拼接方法-->
<!--url?key=value&key=value-->
<a href="/basic?id=1&username=张三&score=10.0&birthday=2020-01-01&sex=1">请求携带数据-基本类型</a>
<hr>
<form action="/basic" method="get">
    id<input type="text" name="id"><br>
    username<input type="text" name="username"><br>
    score<input type="text" name="score"><br>
    birthday<input type="date" name="birthday"><br>
    sex<input type="radio" name="sex" value="1">男
    <input type="radio" name="sex" value="2">女<br>
    <input type="submit" value="基本类型">
</form>

api 工具测试

postman

后端接收

java 复制代码
@Controller
public class Demo2Controller {
    /**
     * 演示1: 接收简单类型数据
     * 接收数据,只需要在方法的参数列表设计参数即可,数据类型对应
     * 接收的前端的name值参数名和方法的参数名保持一致即可
     * -----
     * 小问题: springmvc框架日期,默认只能接收yyyy/MM/dd格式的日期
     * 但是如果前端传递的是yyyy-MM-dd格式的日期,则会报错
     * 解决: 加注解@DateTimeFormat(pattern = "yyyy-MM-dd"),指定解析模版
     */
    @GetMapping("/basic")
    public String basic(int id,
                        String username,
                        double score,
                        @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday,
                        int sex){
        System.out.println(id);
        System.out.println(username);
        System.out.println(score);
        System.out.println(birthday);
        System.out.println(sex);
        return "/ok.html";
    }
}

5.3.2 使用对象接收表单数据 [非常重要]

场景:注册 / 添加 / 更新
实体类,要求实体类的属性和前端 name/key 一致

java 复制代码
@Data // 提供getter/setter/toString/equals/hashCode等方法
public class User {
    private int id;
    private String username;
    private String password;
    private double score;
    @DateTimeFormat(pattern = "yyyy-MM-dd")// 解析日期格式
    private Date birthday;
    private int sex;
}

@Data 注解需要导入下方依赖

xml

XML 复制代码
<!--lombok-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.28</version>
</dependency>

前端

html 复制代码
<h2>对象数据绑定</h2>
<form action="/obj" method="get">
    id<input type="text" name="id"><br>
    username<input type="text" name="username"><br>
    password<input type="text" name="password"><br>
    score<input type="text" name="score"><br>
    birthday<input type="date" name="birthday"><br>
    sex<input type="radio" name="sex" value="1">男
       <input type="radio" name="sex" value="2">女<br>
    <input type="submit" value="对象类型">
</form>

api 工具测试.

后端

java 复制代码
   /**
     * 演示2: 接收对象类型数据
     * 场景: 前端发送的数据量大,可以封装到对象中
     * 前提: 前端发送的数据,必须和对象的属性名保持一致
     */
    @GetMapping("/obj") // 整个项目中请求路径不能重复
    public String obj(User user){
        System.out.println(user);
        return "/ok.html";
    }

5.3.2 使用对象接收 JSON 格式数据 [重点]

一般使用 post 请求,向后端发送 json
前端使用 axios 发送 json

html 复制代码
<!--演示使用axios 发送post请求,且是json数据到后端-->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<button id="btn">点击-aixos发送</button>
<script>
  var btn = document.getElementById("btn");
  btn.onclick=function (){
    console.log("点击事件生效")
    // axios发送post请求
    axios.post('/obj2', {
      id: 2,
      username: '奥特曼',
      password: '123123',
      score:59.9,
      birthday:"2000-01-01",
      sex:1
    }).then(function (response) {
      console.log(response);
    }).catch(function (error) {
      console.log(error);
    });
  }
</script>

接口工具发送 json 数据

后端接收 json

java 复制代码
    /**
     * 接收json格式数据
     * 必须使用对象,且对象的属性名必须和json数据的key保持一致
     * 且需要使用@RequestBody注解,将json数据转换为对象
     */
    @PostMapping("/obj2") 
    public String obj2(@RequestBody User user){
        System.out.println(user);
        // 使用post请求后,默认跳转页面方式是转发,需要重定向跳转,redirect:/ok.html
        return "redirect:/ok.html";
        // 这样可以解决问题,但是一般不会这样用
        // 因为后续是要返回json数据,而不是页面,此处就不用纠结对错...
    }

这里主要强调:前端如果是以 json 的形式将数据发到后端,那么后端使用对象接收前需要使用 注解 @RequestBody

5.3.3 接收数组类型的数据

场景:批量删除需要同时接收多个 id, (前端是复选框的)

delete from tb_user where id in (1,2,3,4)

前端

html 复制代码
<h2>数组绑定</h2>
<form action="/array" method="get">
  <input type="checkbox" name="ids" value="1">1
  <input type="checkbox" name="ids" value="2">2
  <input type="checkbox" name="ids" value="3">3
  <input type="checkbox" name="ids" value="4">4
  <input type="submit" value="数组类型">
</form>

后端

java 复制代码
    /**
     * 自动绑定: 要求前端的请求中的参数要和方法参数名(数组名)一致
     */
    @GetMapping("/array")
    public String array(int[] ids){
        System.out.println("ids = " + Arrays.toString(ids));
        return "ok.html";
    }

5.3.4 List 集合

List 集合使用场景与数组是一样的前端

html 复制代码
<h2>List绑定</h2>
<form action="/list" method="get">
  <input type="checkbox" name="skill" value="Java">Java
  <input type="checkbox" name="skill" value="HTML">HTML
  <input type="checkbox" name="skill" value="Linux">Linux
  <input type="submit" value="List类型">
</form>

SpringMVC 默认是不支持直接封装 List 的,解决方案:

  • 参数前加注解@RequestParam
java 复制代码
    @GetMapping("/list")
    public String list(@RequestParam List<String> skill){
        System.out.println("skill = " + skill);
        return "ok.html";
    }

5.3.5 前端发送数据,后端使用 Map 接收数据

Map 是键值对,键和值一一映射.

跟 Java 对象很类似,属性和属性值一一对应.

所以什么时候需要 / 可以使用 Map 类型来接收参数呢?

  • 凡是可以用对象接收的都可以使用 Map

SpringMVC 默认不支持直接将参数封装进 Map, 需要使用 @RequestParam
前端

html 复制代码
<h2>搜索</h2>
<form action="/map" method="get">
    部门<input type="text" name="dept"> <br>
    手机号  <input type="text" name="phone"> <br>
    日期  <input type="date" name="date"> <br>
    <input type="submit" value="搜索">
</form>

后台

java 复制代码
    /**
     * 演示使用map接收数据
     * 需要使用@RequestParam注解,将map接收数据
     */
    @GetMapping("/map")
    public String search(@RequestParam HashMap<String,Object> map){
        System.out.println(map);
        return "/ok.html";
    }

5.3.6 路径参数 @PathVariable

参考这个路径

这个路径中 weixin_39641494 是用户编号,131625212 是文章 id /myinfo/9527

@GetMapping ("/{userid}/article/details/{aid}

html 复制代码
https://www.baidu.com/s?tn=68018901_3_dg&ie=UTF-8&wd=RESTful
https://blog.csdn.net/article?uid=weixin_39641494&aid=131625212
RESTful风格如下
https://blog.csdn.net/weixin_39641494/article/details/131625212

前端

html 复制代码
<h2>演示: 接收路径中的数据</h2>
<a href="/blog/weixin_39641494/article/details/131625212">
  查看文章详情
</a>

后端

java 复制代码
   /**
     * 接收路径参数
     * 需要使用@PathVariable注解,将路径参数接收封装到参数列表的变量
     * 需要{}中变量名和方法的参数名保持一致
     */
    @GetMapping("/csdn/{uid}/article/details/{aid}")
    public String csdn(@PathVariable String uid, @PathVariable String aid){
        System.out.println("用户id = " + uid);
        System.out.println("文章id = " + aid);
        return "/ok.html";
    }

5.4 页面跳转 [了解] 不用!!!

controller 层类中方法的返回值,定义成 String, 然后 return 中写的就是跳转的页面路径,这个页面必须放在 resources/static/ 下

这样的跳转方式是请求转发,专业写法是

还可以改成重定向

区别是什么?

  • 直观的效果:
    • 请求转发后,浏览器上路径是不会变的
    • 重定向后,浏览器上路径会变的
  • 本质原因
    • 请求转发是服务器内部动作
    • 重定向是浏览器动作

说明:请求转发和重定向技术,在后续前后端分离的开发模式中,已经用不到

因为,前后端分离就是指,后端项目中只有后端 java 代码,不存在页面。即也不需要我们跳转页面...

5.5 会话技术 session

会话:前后端交互会话管理:管理前后端交互过程的数据会话管理技术: session, 基于 cookie

面试题:

  • session 原理,基于 cookie.
    • 第一次请求时,会在后端创建 session 对象,然后随着响应,将 sessionId 存储到 cookie 响应到浏览器
    • 后续每次再请求会带上 sessionId, 找到之前这个 session 对象
  • session 和 cookie 的区别
    • cookie 是浏览器技术,将数据存储在浏览器
    • session 是服务端技术,将数据存储在服务器

想要使用,只需要在 Controller 层方法的参数列表中直接定义该变量即可使用

java 复制代码
package com.qf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
/**
 * 演示使用 session
 */
@Controller
@RequestMapping("/session")
public class Demo4Controller {
    /**
     * 想要使用 session 必须要在方法的参数中添加 HttpSession 类型的参数
     * @param session
     * @return
     */
    @GetMapping("/t1")
    public String test1(HttpSession session){
        System.out.println("---- t1 -----");
        System.out.println("sessionId = " + session.getId());
        // session是存储数据,类似于 map
        // 经验: session登录状态
        session.setAttribute("name","张三");
        return "/ok.html";
    }
    @GetMapping("/t2")
    public String test2(HttpSession session){
        System.out.println("---- t2 -----");
        System.out.println("sessionId = " + session.getId());
        String user = (String) session.getAttribute("name");
        System.out.println("从session取出的 = " + user);
        return "/ok.html";
    }
    /**
     * 销毁 session
     * ps: 说明,session如果不主动销毁,默认保存会话结束. 常见的就是浏览器关闭会话结束.
     */
    @GetMapping("/t3")
    public String test3(HttpSession session){
        System.out.println("---- t3 -----");
        // 销毁session
        session.invalidate();
        return "/ok.html";
    }
}

主要记住用法

  • 对象 HttpSession
  • 存储方法 session.setAttribute ("key",value)
  • 取出数据 session.getAttribute ("key")
  • 销毁对象 session.invalidate ()场景:一般来说,HttpSession 用的多,常用于存储用户的登录信息

5.6 响应:返回 json 数据【重点】

后续工作项目,都是前后端分离开发,前后端使用 JSON 数据交互

  • 前端发送 json, 使用 axios 技术 (类似于 ajax),vue 中就使用 axios 发送请求
  • 后端接收 json, 然后响应给前端 json

前端发送 json 等 vue 时候再演示现在演示响应 JSON 数据 , 非常简单,方法加上@ResponseBody 即可,就会将任何解析为 json 返回

java 复制代码
package com.qf.controller;
import com.qf.model.User;
import com.qf.util.R;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.*;
/**
 * 演示响应json
 * 将后台的数据以json形式返回给前端
 */
@Controller
public class Demo4Controller {
    /**
     * 控制层代码要想返回/响应给前端json数据,只需要
     * 1) 设计方法的返回值为对应类型(String,对象,Map,List)
     * 2) 最后在方法上加上@ResponseBody
     * ---------------------------------------------
     * 真正写项目时,前后端交互的json格式要固定,一般
     * {
     *     code:200,
     *     msg:"xxx",
     *     data:{}
     * }
     * --- 那么就会在java项目中定义该类型的类R
     * --- 前端拿到json后,会获取其中数据
     * if(json.code == 200) {
     *     json.data
     * } else {
     *     alert(json.msg)
     * }
     */
    @GetMapping("/json")
    @ResponseBody // 该注解就会把响应的数据当json返回给前端
    public String testJson(){
        /**
         * json格式
         * {k:v,k:v}
         * {"id":1,"username":"zs"}
         */
        String jsonStr = "{\"id\":1,\"username\":\"zs\"}";
        return jsonStr;
    }
    @GetMapping("/json2")
    @ResponseBody 
    public User testJson2(){
        // 假设这是从数据库查出的数据,并且封装成对象
        User user = new User( );
        user.setId(2);
        user.setScore(1.1);
        user.setPassword("123456");
        user.setUsername("老王");
        user.setBirthday(new Date(  ));
        /**
         * {
         *     id:2,
         *     score:1.1,
         *     password:"123456",
         *     username:"老王",
         *     birthday:
         * }
         */
        return user;
    }
    @GetMapping("/json3")
    @ResponseBody 
    public Map<String, Object> testJson3(){
        HashMap<String, Object> map = new HashMap<>( );
        map.put("id",1);
        map.put("username","老李");
        map.put("password","123456");
        map.put("score",2.2);
        map.put("birthday",new Date(  ));
        return map;
    }
    @GetMapping("/json4")
    @ResponseBody 
    public List<User> testJson4(){
        ArrayList<User> list = new ArrayList<>( );
        list.add(new User());
        list.add(new User());
        list.add(new User());
        /**
         * [
         *  {},
         *  {},
         *  {}
         * ]
         */
        return list;
    }
}

以上这些响应的 json, 但是格式不统一真正开发时,是团队协作

  • 200 成功
  • 400 失败
  • 500 连接超时
  • 300 未登录
  • msg是响应的提示信息
  • data是后端返回给前端的数据

json

java 复制代码
{
  code:200,
  msg:"成功|失败",
  data:{}
}

前后端交互,定义的类,用于统一返回封装数据返回 JSON

java 复制代码
package com.qf.utils;
import lombok.Data;
/**
 * 这个R类,是用来封装响应数据的,一般用于返回json数据
 * 这个类名R,是response的缩写,表示响应数据
 * 也可以Result
 * 若依项目AjaxResult
 * ---------------
 * 这个对象一般包含以下属性:
 * code: 状态码,一般200表示成功,400表示失败
 * msg: 提示信息
 * data: 响应数据
 */
@Data
public class R {
    /**
     * 一般200表示成功,400表示失败
     * 也可以自定义一些
     * 600,请求超时
     * 700,权限不足
     * 800,数据不存在
     * .....
     */
    private Integer code;
    private String msg;
    private Object data;
    public static R ok(){
        R r = new R();
        r.setCode(200);
        r.setMsg("成功");
        return r;
    }
    public static R ok(Integer code,String msg,Object data){
        R r = new R();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }
    public static R ok(Object data){
        R r = new R();
        r.setCode(200);
        r.setMsg("成功");
        r.setData(data);
        return r;
    }
    public static R fail(String msg){
        R r = new R();
        r.setCode(400);
        r.setMsg(msg);
        return r;
    }
    public static R fail(){
        R r = new R();
        r.setCode(400);
        r.setMsg("失败");
        return r;
    }
}

java

java 复制代码
    /**
     * 最终版
     */
    @GetMapping("/t4")
    @ResponseBody
    public R test4() {
        // R r = new R();
        // r.setCode(200);
        // r.setMsg("成功");
        // r.setData("hello");
        // return R.ok(new User());
        return R.ok(2000,"查询用户成功",new User());
        // return R.fail();
    }

补充:如果该类中所有方法都返回 json, 那就需要在每个方法上都要加 @ResponseBody 注解,有点麻烦,

此时可以直接将 @Controller 换成 @RestController, 以后方法默认返回 json, 就不需要加 @ResponseBody

java 复制代码
// @Controller
// @ResponseBody // 整个类中的所有方法,都返回json
// 以上两个注解,可以简写成一个
@RestController
public class Demo5Controller {
}

5.7 拦截器

拦截器拦的请求,主要拦截请求判断一些数据,决定要不要放行

保安...使用步骤

  • 编写自定义拦截器类
  • 实现接口 HandlerInterceptor
  • 重写拦截方法 preHandler ()
  • 配置拦截器
    • 自己定义一个配置类,实现 WebMvcConfigurer 接口,重写方法 addInterceptors
    • 另外,该配置类上加了一个非常重要的注解 @Configuration

创建拦截器包 interceptor, 自定义拦截器类

java 复制代码
package com.qf.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 自定义拦截器
 * Handler单词是处理器,其实就是Controller中的方法
 */
@Component//创建对象
public class MyInterceptor implements HandlerInterceptor {
    /**
     * 重点关注这个方法,是前置拦截器,返回true表示放行,返回false表示不放行
     * @param request 请求对象,可以获取请求的信息(数据,路径,方法,请求头,session...)
     * @param response 响应对象
     * @param handler 就是目标Controller的方法对象
     * @return 返回true表示放行,返回false表示不放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法被调用");
        return true;
    }
}

创建配置文件的包 config, 其中创建拦截器配置类

java 复制代码
package com.qf.config;
import com.qf.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * 自定义拦截器配置类
 */
@Configuration // 这个注解表示当前类是一个配置类,会被Spring扫描到
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Autowired // 注入
    private HandlerInterceptor myInterceptor;
   // 向容器注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry.addInterceptor(myInterceptor).addPathPatterns("/**");
        // registry.addInterceptor(myInterceptor).addPathPatterns("/json/**");
        // 拦截所有请求,除了/session/**
        registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/session/**");
    }
}

发请求测试即可

实战演示:身份信息认证设置身份认证拦截器,对所有请求拦截,判断有没有登录,登录过就放行,没有登录就返回错误提示

设置登录。退出接口,用于存储 / 销毁 session

java 复制代码
package com.qf.controller;
import com.qf.utils.R;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;


@RestController
@RequestMapping("/user")
public class UserController {
    
    //登录 
    @RequestMapping("/login")
    public R login(HttpSession session){
        // 登录成功,将用户信息存储到session中
        session.setAttribute("user","lisi");
        return R.ok();
    }
    // 退出登录
    @RequestMapping("/logout")
    public R logout(HttpSession session){
        // 退出登录,将session中的用户信息删除
        session.invalidate();
        return R.ok();
    }
}

设置拦截器,判断有无登录信息,决定要不要

java 复制代码
package com.qf.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.qf.utils.R;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;


@Component
public class AuthInterceptor implements HandlerInterceptor {
    /**
     * 拦截请求,判断有无登录,如果没有登录,则返回json,R,提示没有登录
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if (user == null){ // 没有登录信息,不放行,还有返回错误信息
            // 设置响应格式为json
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            // 将R对象转换为json字符串,使用fastjson工具,需要导入依赖
            R r = new R();
            r.setCode(500);
            r.setMsg("没有登录,禁止访问");
            String jsonString = JSONObject.toJSONString(r);
            // 这个将字符串写出到浏览器
            writer.write(jsonString);
            return false;
        }
        return true; // 登录成功,放行
    }
}

上面需要 fastjson 工具类,所有需要导包

xml

XML 复制代码
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version> <!-- 使用最新版本 -->
        </dependency>

拦截器配置类

java 复制代码
package com.qf.config;
import com.qf.interceptor.AuthInterceptor;
import com.qf.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 *自定义拦截器配置类
 */
@Configuration // 这个注解表示当前类是一个配置类,会被Spring扫描到
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Autowired // 注入
    private MyInterceptor myInterceptor;
    @Autowired
    private AuthInterceptor authInterceptor;
   // 向容器注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry.addInterceptor(myInterceptor).addPathPatterns("/**");
        // registry.addInterceptor(myInterceptor).addPathPatterns("/json/**");
        // 拦截所有请求,除了/session/**
        registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/session/**");
        registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/user/**");
    }
}

测试即可

  • 先不登录,访问任意路径查看效果
  • 登录后,再访问
  • 退出登录,销毁 session, 再测试

5.8 全局异常处理

SpringBoot 中有一个 @ControllerAdvice 的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用 @ExceptionHandler 注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。

java 复制代码
package com.qf.utils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice // 异常处理类,这个注解可以捕获Controller层抛出的异常
public class MyExceptionHandler {
    /**
     * 处理Controller层抛出的异常
     * 方法的参数,就是捕获到的异常对象
     */
    // @ExceptionHandler(RuntimeException.class)  //指定捕获异常
    @ExceptionHandler // 捕获任意异常
    // @ResponseBody
    public R handleException(Exception e){
        R r = new R();
        r.setCode(500);
        r.setMsg("系统错误,请联系管理员,"+e.getMessage());
        // 捕获到异常,可以做很多事情
        // 可以返回json提示
        // 也可以跳转一个页面
        // 可以记录日志
        // 可以发送邮件
        // 可以发送短信
        // 可以记录到数据库
        return r;
    }
}

测试发请求,在 Controller 中故意写错代码,测试即可

补充:接口工具使用

目前情况,写好了 controller 层接口,写 html 页面,写 a 标签或者表单测试,--> 这样不专业!!!

因为项目是前后端分离,没有前端前端项目,没法写页面测试都要使用接口测试工具!

接口:不是 interface, 是前后端对接的接口,即 controller-service-dao 一套

idea插件

安装

一般需要重启一次idea

测试发请求

postman工具

  • 安装

  • 注册

  • 汉化

参考文档: https://apifox.com/apiskills/postman-chinese/

1将汉化包解压到 Postman 安装目录到 resources 下

2Windows系统,进入Postman安装地址/版本/resources目录。

3找安装地址的方式:

桌面找到 Postman应用程序 -> 右键 -> 打开文件所在位置 -> 进入app-..*/resources。

默认安装地址:C:/Users/用户名/AppData/Local/Postman。

4 解压汉化版到/resources下

基本使用

高端使用1

  • 设计集合,存储每次的请求

测试完,点击保存到这个集合

高端使用2:

  • 设计环境变量

即抽取一些变量,重复使用,比如说抽取 每次请求前的服务器地址http://localhost:8080 , 重复使用

  1. 创建变量
  1. 使用变量
相关推荐
wadesir2 小时前
Judy数组:C语言中的高性能动态数组(全面入门Judy库使用指南)
c语言·开发语言
csbysj20202 小时前
SQLite Glob 子句详解
开发语言
Seven972 小时前
字符串匹配算法
java
ss2732 小时前
阻塞队列:生产者-消费者模式
java·开发语言
Fcy6482 小时前
C++ set和multiset的使用
开发语言·c++·stl·map·multimap
八个程序员2 小时前
c++常见问题1——跳出代码
开发语言·c++
艾莉丝努力练剑2 小时前
【Linux进程(一)】深入理解计算机系统核心:从冯·诺依曼体系结构到操作系统(OS)
java·linux·运维·服务器·git·编辑器·操作系统核心
Kiri霧2 小时前
Go 字符串格式化
开发语言·后端·golang
guslegend2 小时前
SpringBoot 缓存深入
java