回顾:
会话技术
Cookie: 将用户的数据保存在浏览器中
new Cookie 创建
response.addCookie(cookie) 将cookie回写给浏览器
Cookie[] request.getCookies() 获取所有cookie
设置最大存活时间setMaxAge(秒) 秒>0 表示存活时间 ,=0 立即删除 , <0 则浏览器关闭之后删除
getName
getValue
Session: 将数据保存在服务器中
需要依赖cookie技术,JSEESIONID
当用户第一次访问服务器的时候,只要调用request.getSession(),服务器就会创建一个Session,并且将Jsessionid通过cookie回写给浏览器
当用户第二次访问服务器的时候,浏览器会自动携带jsessionid给服务器,如果用户调用了request.getSession,那么将会获得第一次创建的那个session对象
setAttribute
getAttribute
销毁细节:
invalidate
超时30分钟自动销毁
服务器关闭
用户访问服务器,服务器调用了request.getSession,然后用户关闭了浏览器,请问服务器中Session销毁了?
作用范围:
在一次会话中,保存用户独有的信息
1.过滤器
2.RESTFul API 只是一种规则,或则一种命名规范 abccded Demo.java
包名:全小写
类名:驼峰式命名
一、Filter
1. 什么是Filter
filter 翻译过来是: 过滤器。有点像生活中的收费站一样。当请求从浏览器发出到达服务器的时候,过滤器会最先捕获到请求,我们在过滤器里面对请求进行过滤操作。如果过滤器拒绝放行,那么请求讲无法到达服务器代码 , 只有过滤器放行,服务器代码才会执行。过滤器由于是在请求到达之前先开始工作,所以可以使用它来处理敏感字符,或者对权限进行校验、自动登录
2. Filter 入门
- 编写Filter
~~~java
@WebFilter(“/*”) //过滤所有的路径,也就是所有的请求都会抓住
public class MyFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init~!~");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter~!~");
//放行
chaing.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("destroy~!~");
}
}
~~~
- 测试
~html
访问首页即可: localhost:8080/index.html
~
3. Filter 特点
- Filter 可以抓住任意请求,包括 controller ,也包含html资源…
- filter可以选择放行,也可以选择不放行。
- 如果不放行,filter可以对该请求做出响应。
- 如果放行,那么controller执行完毕后,还会执行放行后面的代码。
4. Filter 生命周期
生命周期 , 表示事物从出生到消亡经过的时间段。 filter 里面实现了三个方法
init()
|doFilter
|destroy
, 这三个方法正好对应了filter的三个声明周期方法。
- init
服务器启动的时候就执行,并且只会执行一次。 用于一些初始化工作。
- doFilter
核心方法 : 用于处理拦截具体操作 , 可以执行多次, 来一次请求,执行一次。
- destroy
在这个方法中,可以释放过滤器使用的资源。 正常关闭服务器 |从服务器中移除项目时调用。
5. 过滤器链
在项目中我们可以声明很多个过滤器, 这些过滤器在处理请求是有先后顺序的,工作机制好比我们生活中在路上见到的收费站一样,如果前一个收费站没有放行,后面的收费站是无法捕获请求的。 如下图所示,请求到来时,先后经过三个过滤器,如果大家都放行,那么请求到达我们的controller。 其实过滤器也属于server端的一份子,此处为了形象化,才把它画在中间位置。 默认情况下,我们标记过滤器,使用的是@WebFilter ,如果是这样的话,就无法确定先后顺序。具体确定Fitler的位置,请参看在SpringBoot文档
73.1.1
章节。
- 编写两个过滤器
~~~java
public class AFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.err.println("init~~");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//放行之前
System.err.println("doFilter~~");
chain.doFilter(request , response);
//放行之后~
}
@Override
public void destroy() {
System.err.println("destroy~!~");
}
}
public class BFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.err.println("init~~");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//放行之前
System.err.println("doFilter~~");
chain.doFilter(request , response);
//放行之后~
}
@Override
public void destroy() {
System.err.println("destroy~!~");
}
}
~~~
注意: 上面的两个过滤器并没有打上任何的注解,如果不做任何的配置,他们仍然只是普通类。 咱们的SpringBoot并不认识两个过滤器,为了能够控制他们的先后顺序,我们需要使用独立的代码来配置。
- 配置过滤器
编写filter配置类 , 使用@Configuration 和 @Bean 来配置
~~~java
@Configuration //配置的注解,启动服务器,会创建出来该类实例,Spring容器持有它。
public class MyFilterConfiguration {
@Bean("bean1") //启动服务器会执行含有bean注解方法,获取方法返回值后,并且Spring容器中持有它
public FilterRegistrationBean registrationBean(){
System.err.println("----registrationBean()----");
//声明过滤器注册bean对象
FilterRegistrationBean bean = new FilterRegistrationBean();
//设置配置的是哪一个过滤器
bean.setFilter(new MyFilter());
//设置该过滤器匹配的路径
bean.addUrlPatterns("/*");
//设置过滤器的order值,越小越前面
bean.setOrder(1);
return bean;
}
@Bean("bean2")
public FilterRegistrationBean registrationBean2(){
System.err.println("----registrationBean2()----");
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new AMyFilter02());
bean.addUrlPatterns("/*");
bean.setOrder(2);
return bean;
}
}
~~~
使用@WebFilter来配置过滤的路径,需要加上@ServletComponentScan,但是无法制定顺序.如果想要指定顺序,可以使用代码的方式来注册
小结:
Filter : 过滤器, 可以对请求和响应做出一些处理。例如对请求进行数据的校验, 权限校验
@WebFilter("要过滤的路径")
要想让注解生效,我们需要在程序入口类上面加一个@ServletComponentScan
采用注解方式的执行顺序是按照类名字母顺序
生命周期方法:
init : 项目启动的时候
doFilter : 匹配到要拦截的路径的时候执行
chain.dOFilter 放行
destroy : 销毁 , 项目关闭的时候调用
过滤器链相关:
@Configuration 将类交给Spring去托管/创建
@Bean 将方法交给Spring去调用
FilterRegistrationBean
setFilter
addUrlPatterns
setOrder : 设置顺序
6. 自动登录(案例)
1. 分析
2. 搭建环境
- 添加依赖库
~~~groovy
compile(“org.thymeleaf:thymeleaf-spring4:3.0.9.RELEASE”)
compile(“org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE”)
记得在模板页面,引入命名空间:
~~~
- 登录页面
~~~html
用户名:
密 码:
自动登录
~~~
3. 基本登录实现
~java
@RequestMapping("user_login")
public String login(String username , String password , HttpSession session){
if("admin".equals(username) && "123".equals(password)){
System.out.println("登录成功");
}
System.out.println("登录失败");
}
~
4. 登录细节处理
- 成功和失败细节
~~~java
@RequestMapping(“user_login”)
public ModelAndView login(String username , String password , String auto,
HttpSession session , HttpServletResponse response) throws InterruptedException {
System.out.println("执行登录操作了-=="+auto);
ModelAndView modelAndView = new ModelAndView();
//1. 判定账号密码
if("admin".equals(username) && "123".equals(password)){
System.out.println("登录成功");
modelAndView.setViewName("index");
//如果使用model对象来存,那么只有这个请求才能看到数据
//modelAndView.addObject("username",username);
//一般登录成功之后,用户的数据都是往session里面存,因为session是一次会话有效。
session.setAttribute("username",username);
//判定是否有自动登录
if("on".equals(auto)){
//把账号和密码存储到cookie里面去。 以便下次来的时候,带上账号密码, 然后在后台完成登录。
Cookie cookie = new Cookie("account",username+"#"+password);
cookie.setMaxAge(60*60*24*7);//7天内自动登录。
response.addCookie(cookie);
}
return modelAndView;
}
System.out.println("登录失败");
//1. 回到登录页面 , 2. 显示失败的原因
modelAndView.setViewName("login");
modelAndView.addObject("msg","用户名或者密码错误!");
return modelAndView;
}
~~~
5. 过滤器实现
~~~java
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//为了避免重复执行自动登录的逻辑,需要判断session中是否已经有username
String usernameValue = (String) req.getSession().getAttribute("username");
if(usernameValue != null) { //表示已经登录过了
chain.doFilter(request, response);
return ;
}
//如果登录相关的请求,则直接放行
String path = req.getRequestURI();
System.out.println("path:"+path);
if(path.contains("login")){
chain.doFilter(request, response);
return ; // 下面的代码不再执行了
}
String cookieValue = null;
//1.获取指定名称的cookie autologin
Cookie[] cookies = req.getCookies();
if(cookies != null) {
for (Cookie cookie : cookies) {
if ("autologin".equals(cookie.getName())){
cookieValue = cookie.getValue();
break;
}
}
}
boolean isLogin = false;
//2.校验cookie中的value zhangsan#123
if(cookieValue != null){
String[] split = cookieValue.split("#");
String username = split[0];
String password = split[1];
//校验用户名和密码
if("zhangsan".equals(username)&&"123".equals(password)){
//校验成功,保存用户登录的状态
HttpSession session = req.getSession();
session.setAttribute("username",username);
//标记自动登陆成功
isLogin = true;
}
}
//判断是否校验成功
if(isLogin){
chain.doFilter(request,response);
}else{
//不放行,跳转去登录页面
resp.sendRedirect("/loginUI");
}
}
~~~
二、 RESTFul API
早前给大家提过,我们的的应用不是C/S
就是 B/S
结构,为了让两端更好的对接,服务器端通常都需要制定出通俗易懂的一套API范式, 也就是客户端通过什么地址来访问服务器端的方法。
比如我们需要根据用户id查询用户的信息
A 程序员给出的路径API地址是: localhost:8080/user_findById?id=6
B 程序员给出的API路径是:localhost:8080/u_getUser?id=6
当然还有可能还有C程序员的地址路径,这就会让我们在不同的公司工作,必须适应不同程序员的编码风格,那么就有没有一种统一的规范、让所有的程序员都参照这种规范来制定、编写API 呢?其实这种规范有
1. 什么是REST Ful API
REST即
Representational State Transfer
的缩写,可译为”表现层状态转化”。最初由`Roy T. Fielding(HTTP/1.1协议专家组负责人)在其2000年的博士学位论文中提出。HTTP就是该架构风格的一个典型应用。从其诞生之日开始,它就因其可扩展性和简单性受到越来越多的架构师和开发者们的青睐。一方面,随着云计算和移动计算的兴起,许多企业愿意在互联网上共享自己的数据、功能;另一方面,在企业中,RESTful API(也称RESTful Web服务)也逐渐超越SOAP成为实现SOA的重要手段之一。时至今日,RESTful架构风格已成为企业级服务的标配。说的通俗一点它只是一种风格而已。基于这种风格编码,更容易理解,更有层次感 , 它主要针对的是我们请求的路径写法规范
网络上的一切数据都看成是资源。
2. REST Ful API的必要性
- 传统方式操作资源
http://localhost:8080/项目/user_findById?id=1 查询 ,GET请求
http://localhost:8080/项目/user_save 新增 ,POST请求
http://localhost:8080/项目/user_update 更新 ,POST请求
http://localhost:8080/项目/user_delete?id=1 删除 ,GET | POST请求
这种传统方式如果团队只有一个人开发 , 有时候看路径地址自己看着也是挺好认的。但是如果团队的人多了,那么为了很好的辨识每一个地址路径做的是什么事情,并且阅读起来很方便,那么就需要详细的约束文档描述,或者有足够的团队默契。
- REST Ful API方式操作资源
REST 的风格根据Http中的8种请求(GET | POST | PUT | DELETE | OPTIONS | HEAD | TRACE | CONNECT)中摘取了4个 (GET | POST | PUT | DELETE)来表示我们目前的CRUD 操作。所以早前我们说过,Rest Ful 其实是一种风格、不是标准。因为它拿着4个对应的请求指令来对应我们的增删改查,并且它认为在网络上一切都可以成为资源。 光从下面的4个路径来看,很难分辨出到底执行的是什么操作,需要配合请求方式来
~~~html
http://localhost:8080/项目/users/1 查询资源 ,GET请求
http://localhost:8080/项目/users 新增资源 ,POST请求 —> 建立新的记录 , 新的数据
http://localhost:8080/项目/users 更新资源 ,PUT请求 —> 修改以前的数据
http://localhost:8080/项目/users/1 删除资源 ,DELETE请求
~~~
3. RestFul API 简单使用
- controller
~~~java
@RestController
public class UserController {
private static final String TAG = “UserController”;
//只会处理post请求
@RequestMapping(value = "/users",method = RequestMethod.POST)
public String register(User user){
System.out.println("user=" + user);
return "register success";
}
}
~~~
4. Rest Ful API 特点
Rest API 是一种规范、一种标准,我们最好在制定API的时候,遵循以下规则。Rest API 应用得最好的是
github
大家可以参照https://developer.github.com/v3/ , 比如这个链接 https://github.com/facebook/fresco/issues/1214 :即表示访问github上的facebook仓库中的fresco项目,编号为1214的issues
- 每个网址代表一种资源(resource),所以网址中不能有动词 ,只能有名词。
- 使用小写字母 - 尽管 URI 不区分带小写,但是在 url 中使用小写字母是一种很好的做法。
- 使用复数名词 - 使用复数名词定义资源。比如,我们使用 users 标识用户资源。
- 避免使用空格 - 处理长资源名时使用下划线(_)或者连字符(-),比如,用 authorized_users 而不是 authorizedusers。
- 匹配 GET | POST | PUT | DELETE 请求方式
下面是些例子:
~html
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息 GET /user/3
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物 GET /school/10086/teacher | student
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
~
5. REST FUL 演练
接下来我们使用Rest ful API 来改造第一天我们写过的controller,并且前两天也学过了Thymeleaf, 现在就综合页面来练练吧。
1. 添加学生
~~~java
private static Map
2. 删除学生
~~~java
//删除学生
@DeleteMapping(“/students/{id}”)
public String delete(@PathVariable int id){
map.remove(id);
System.out.println("删除后,打印学生如下");
Set<Integer> keySet = map.keySet();
for(int key : keySet){
Student student = map.get(key);
System.out.println(key + " === " + student);
}
return "delete success";
}
~~~
3. 更新学生
~~~java
//更新学生
@PutMapping(“/students/{id}”)
public String update(@PathVariable int id , Student student){
System.out.println("update --" + id);
System.out.println("before -=--" + map);
map.put(id , student);
System.out.println("after -=--" + map);
return "update success";
}
~~~
4. 查询学生
~~~java
//查询所有学生
@GetMapping(“/students”)
public String list(){
System.out.println("查询所有学生:" + map);
return "list success";
}
//查询单个学生
//localhost:8080/sutdents --- 所有
//localhost:8080/students?id=2 --- 查询单个
//localhost:8080/students/2 --- 查询单个 -- restful
// "message": "Missing URI template variable 'id' for method parameter of type int",
@GetMapping("/students/{id}")
public String findById(@PathVariable int id){
System.out.println("id=" + id);
Student stu = map.get(id);
return "findById success~!~"+stu;
}
~~~
RestFul 仅仅只是一种编写URL的规范
增加 : POST /students
删除 : DELETE /students/{sid}
修改 : PUT /students
查询: GET /students/{sid}
/students
Filter :
@WebFilter("要拦截的路径")
要想让这个注解生效 , 在入口类上打一个注解@ServletComponentScan 扫描当前包以及当前包的子包中所有的有WebFilter注解的类,将它加载进来交由spring进行管理
生命周期方法:
init: 程序启动时执行
doFilter : 匹配到路径的时候执行
放行: chain.doFilter(req,resp)
destroy : 销毁的方法, 服务器退出时这个方法会执行
过滤器链: 多个过滤器按照一定顺序执行
(代码方式注册过滤器,指定顺序)
案例 : 自动登录的案例