Java程序员需要掌握的前端知识(一)

对于前端知识,需要进一步巩固和加强,进入企业之后,要具备一定的接口调试,参数接收的能力,以及单体页面的开发,这里我学习一下前端知识巩固一下自身的技术栈和水平。本次笔记是跟学黑马的同名课程,巩固一下自己的前端知识水平,希望入职能用到。

前言

Java 程序员一提起前端知识,心情那是五味杂陈,百感交集。

  • 说不学它吧,说不定进公司以后,就会被抓壮丁去时不时写点前端代码
  • 说学它吧,HTML、CSS、JavaScript 哪个不得下大功夫才能精通?
  • 学一点够不够用呢?如果只学基础的 JavaScript 是不够用的,前端都已经工程化了,Vue、React 这些框架你去看吧,光有点基础根本看不懂,甚至连前端页面路径在哪儿配置,如何跳转都不甚了解,所以得学,而且要学的还不少,请把前端当作 web 不可或缺的一部分来学习。
  • 学习前端好处挺多,我听说过这么一句挺有道理的话:一个程序员至少应该掌握一门静态语言,如 Java,还应该掌握一门动态语言,如 JavaScript。而且,你不觉得学了前端,就如打通了程序员的任督二脉,可以独立接活了嘛

这门课为什么不由前端老师来讲?

  • 前端老师不知道后端学员的痛点,他认为重要的咱不关心,他认为是常识一带而过的又恰恰是咱迷茫的地方,作为后端老师,我更懂后端学员
  • 前端老师不懂后端知识,只能用前端思维来讲前端,如果授课老师懂 Java,能用一些现有知识对比讲解,能起到快速突破的效果
  • 前端每个框架动辄需要数十个小时的学习,咱耗不起,我们更希望学到对我们最有用的那部分,其它的省省吧,总不能夺了前端程序员的饭碗

课程安排

  • 整个课程分成五章
    • HTML / CSS 这部分对咱们来说,不是重点,但又不能不讲,这俩知识作为第一章,必学
    • JavaScript 这部分是重点,尤其是 ES6 以后的一些新语法,不理解这些,前端代码你根本看不懂,必学
    • Vue2,Vue3,React 这三章是三选一的关系,根据你入职公司的使用的前端技术不同,有针对地学习
      • 后三章会涵盖 TypeScript、VueCli、Vuex、VueRouter、ElementUI、Vite、CreateReactApp、React、Redux、ReactRouter 等库和工具的使用
    • jquery 经过调研,还有一些学员毕业后确实会用到,所以也作为一个可选章节进行学习

课程安排

在这里插入图片描述

课程目标

在这里插入图片描述

第一章. HTML 与 CSS

HTML 是什么:即 HyperText Markup language 超文本标记语言,咱们熟知的网页就是用它编写的,HTML 的作用是定义网页的内容和结构。

什么是HyperText呢,我们看下资料提供的课件。
详见 代码\第1章\example1-什么是html和css 1.html
在这里插入图片描述
点击网页2
在这里插入图片描述

点击网页3
在这里插入图片描述

那什么是Markup language呢?
我们右键,以VS Code打开。
在这里插入图片描述
这些以尖括号括起来的部分,我们都称之为Markup。在前端中,我们称之为标签。
在这里插入图片描述

  • HyperText 是指用超链接的方式组织网页,把网页联系起来
  • Markup 是指用 <标签> 的方式赋予内容不同的功能和含义

CSS 是什么:即 Cascading Style Sheets 级联(层叠)样式表,它描述了网页的表现与展示效果

我们引入style.css样式。
在这里插入图片描述
可以看到超链接的字体和背景颜色发生了变化。
在这里插入图片描述

1. HTML 元素

HTML 由一系列元素 elements 组成,例如

<p>Hello, world!</p>
  • 整体称之为元素

  • <p></p> 分别称为起始标签结束标签

  • 标签包围起来的 Hello, world 称之为内容

  • p 是预先定义好的 html 标签,作用是将内容作为一个单独的段落

在 代码\第1章\example2-html元素下新建文件3.html我们自己写一个元素
在这里插入图片描述
代码如下:

<p>Hello, world!</p>

用浏览器打开
在这里插入图片描述
效果如下:
在这里插入图片描述
如果我们想增加标签数量,通过快捷键复制
ALT + SHIFT + ↓
在这里插入图片描述
展示结果如下:
在这里插入图片描述
这里小提示:如果我们熟悉IDEA的快捷键,我们可以通过下载插件,改变快捷键习惯为IDEA的
在这里插入图片描述

元素还可以有属性,如

<p id="p1">Hello, world! 1</p>
<p id="p2">Hello, world! 2</p>
<p id="p3">Hello, world! 3</p>
<p id="p4">Hello, world! 4</p>
  • 属性一般是预先定义好的,这里的 id 属性是给元素一个唯一的标识
    在这里插入图片描述
    除此属性之外,我们还可以增加title标题属性
<p id="p1" title="标题1">Hello, world! 1</p>
<p id="p2">Hello, world! 2</p>
<p id="p3">Hello, world! 3</p>
<p id="p4">Hello, world! 4</p>

网页展示如下:鼠标悬浮后,出现标题1
在这里插入图片描述

元素之间可以嵌套,如

<p id="p1" title="标题1">Hello, world! 1 I am <b>Cony</b></p>
<p id="p2">Hello, world! 2</p>
<p id="p3">Hello, world! 3</p>
<p id="p4">Hello, world! 4</p>

在这里插入图片描述

错误嵌套写法:
标签不能交叉

<p>HTML 是一门非常<b>强大的语言</p></b>

不包含内容的元素称之为空元素,如

<img src="1.png">
<img src="1.png"/>
  • img 作用是用来展示图片
  • src 属性用来指明图片路径

在这里插入图片描述

2. HTML 页面

前面介绍的只是单独的 HTML 元素,它们可以充当一份完整的 HTML 页面的组成部分

在这里插入图片描述

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>测试页面</title>
  </head>
  <body>
    <p id="p1">Hello, world!</p>
    <img src="1.png">
  </body>
</html>
  • html 元素囊括了页面中所有其它元素,整个页面只需一个,称为根元素
  • head 元素包含的是那些不用于展现内容的元素,如 titlelinkmeta(指定编码)
  • body 元素包含了对用户展现内容的元素,例如后面会学到的用于展示文本、图片、视频、音频的各种元素

在 代码\第1章\example3-html页面 目录下新建网页文件2.html
在这里插入图片描述
快捷键!,可以直接生成html的模版
在这里插入图片描述
这里我们修改代码为:

<!DOCTYPE html>
<!-- 网页展示为:中文(默认) -->
<html lang="zh">

<head>
    <!--  字符集编码 -->
    <meta charset="UTF-8">
    <!-- 浏览器的兼容模式(这里不用关心) -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 视口 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>这是一个完整页面</title>
</head>

<body>
    <p>Hello World</p>
    <img src="1.png">
</body>

</html>

用浏览器打开后的效果如下:
在这里插入图片描述

图片宽度比较大,我们重新设置一下宽度的像素

<img src="1.png" width="300">

再看浏览器如下:
在这里插入图片描述

3. 常见元素

1) 文本

Heading

<h1>1号标题</h1>
<h2>2号标题</h2>
<h3>3号标题</h3>
<h4>4号标题</h4>
<h5>5号标题</h5>
<h6>6号标题</h6>

效果如下:
在这里插入图片描述

Paragraph

<p>段落</p>

代码如下

<p id="p1" title="悬浮1">段落1</p>
    <p id="p2" title="悬浮2">段落2</p>
    <p id="p3" title="悬浮3">段落3</p>

效果:
在这里插入图片描述

List

无序列表 unordered list

<ul>
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
</ul>

在这里插入图片描述

有序列表
ordered list

<ol>
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
</ol>

在这里插入图片描述

多级列表

<ul>
    <li>
    	北京市
        <ul>
            <li>海淀区</li>
            <li>朝阳区</li>
            <li>昌平区</li>
        </ul>
    </li>
    <li>
    	河北省
        <ul>
            <li>石家庄</li>
            <li>保定</li>
        </ul>
    </li>
</ul>

在这里插入图片描述

Anchor

锚,超链接

<a href="网页地址">超链接文本</a>
(1).跳转本地网页

本地网页(同目录)

<a href="2.html">本地网页</a>

在这里插入图片描述

(2).跳转互联网网页
<a href="http://www.baidu.com">互联网网页</a>

在这里插入图片描述

单击后
在这里插入图片描述

(3).跳转页面内锚点
<a href="#p1">页面内锚点</a>
    <hr>
    <br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br>
    <p id="p1">很下面的内容 <a href="#">回到顶部</a></p>

在这里插入图片描述
点击后,到p1所在的标签位置
在这里插入图片描述

2) 多媒体

Image

<img src="文件路径">

src 格式有 3 种

  • 文件地址

比如

<img src="heima.png">

在这里插入图片描述

  • data URL,格式如下
  data:媒体类型;base64,数据

这里要把图片转换为Base64的二进制数据,具体方法如下

 @Test
    public void test(){
    
    
        String path = "E:\\黑马\\前端\\代码\\代码\\第1章\\example4-常见元素\\heima.png";
        byte[] bytes = null;
        try {
    
    
            Path path2 = Paths.get(path);
            bytes = Files.readAllBytes(path2);
            // 转换成BASE64Encoder
            BASE64Encoder encoder = new BASE64Encoder();
            String content = encoder.encode(bytes);
            System.out.println(content);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

输出结果如下:
在这里插入图片描述
页面展示如下:
在这里插入图片描述

  • object URL,需要配合 javascript 使用
    这个后续js的时候介绍

Video

<video src="文件路径"></video>

代码如下:

<!-- controls是显示播放进度条 -->
<video src="test.mp4" width="300" controls autoplay></video>

页面:
在这里插入图片描述

Audio

<audio src="文件路径"></audio>

代码如下:

<audio src="bgm.mp3" controls></audio>

页面如下:

在这里插入图片描述
这里推荐一个网站,遇事不决MDN
MDN
在这里插入图片描述

3) 表单

作用与语法

表单的作用:收集用户填入的数据,并将这些数据提交给服务器

表单的语法

<form action="服务器地址" method="请求方式" enctype="数据格式">
    <!-- 表单项 -->
    
    <input type="submit" value="提交按钮">
</form>
  • method 请求方式有
    • get (默认)提交时,数据跟在 URL 地址之后
    • post 提交时,数据在请求体内
  • enctype 在 post 请求时,指定请求体的数据格式
    • application/x-www-form-urlencoded(默认)
    • multipart/form-data
  • 其中表单项提供多种收集数据的方式
    • 有 name 属性的表单项数据,才会被发送给服务器

常见的表单项

文本框
<input type="text" name="uesrname">

这里我们自己写一个表单

<form action="https://www.baidu.com/s">
        <input type="text" name="wd">
        <input type="submit" value="搜索">
    </form>
    <hr>

页面如下:
在这里插入图片描述

加深难度,结合springboot
前端:

<form action="http://localhost:8077/test">
        <input type="text" name="username">
        <input type="submit" name="提交">
    </form>

后端代码:

@Controller
public class MyController {
    
    

    @RequestMapping("/test")
    @ResponseBody
    public String test(String username) {
    
    
        System.out.println("username:" + username);
        return "收到数据";
    }
}

这里 @Controller + @ResponseBody 可以用@RestController替代
前端页面:
在这里插入图片描述

点击提交后,后端运行结果如下:
在这里插入图片描述

密码框
<input type="password" name="password">

增加密码框

<form action="http://localhost:8077/test">
        账号:<input type="text" name="username">
        <hr>
        密码:<input type="password" name="password">
        <input type="submit" name="提交">
    </form>

页面如下:
在这里插入图片描述

隐藏框
<input type="hidden" name="id">

页面中没有显示
在这里插入图片描述
那如果我们想通过隐藏框传递值呢,这里我们可以给隐藏框赋值。

<input type="hidden" name="id" value="1">

前台代码如下:

 <form action="http://localhost:8077/test">
        账号:<input type="text" name="username">
        <hr>
        密码:<input type="password" name="password">
        <br>
        <input type="submit" name="提交">
        <hr>
        <input type="hidden" name="id" value="1">
    </form>

后台代码:

    @RequestMapping("/test")
    public String test(User user) {
    
    
        System.out.println("username:" + user.getUsername());
        System.out.println("id:" + user.getId());
        return "收到数据";
    }

新建一个实体类

/**
 * @ClassName: User
 * @Description:
 * @Author: wty
 * @Date: 2023/7/13
 */

public class User {
    
    
    private String username;
    private String password;
    private Integer id;
    private LocalDate birthday;

    public String getUsername() {
    
    
        return username;
    }

    public void setUsername(String username) {
    
    
        this.username = username;
    }

    public String getPassword() {
    
    
        return password;
    }

    public void setPassword(String password) {
    
    
        this.password = password;
    }

    public Integer getId() {
    
    
        return id;
    }

    public void setId(Integer id) {
    
    
        this.id = id;
    }

    public LocalDate getBirthday() {
    
    
        return birthday;
    }

    public void setBirthday(LocalDate birthday) {
    
    
        this.birthday = birthday;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", id=" + id +
                ", birthday=" + birthday +
                '}';
    }
}

页面展示:
在这里插入图片描述

输出如下:
在这里插入图片描述

日期框
<input type="date" name="birthday">

后台增加属性

@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;

public LocalDate getBirthday() {
    
    
        return birthday;
    }

    public void setBirthday(LocalDate birthday) {
    
    
        this.birthday = birthday;
    }

在这里插入图片描述
后台输出如下:
在这里插入图片描述

单选
<input type="radio" name="sex" value="" checked>
<input type="radio" name="sex" value="">

页面如下:

<!-- 性别 --><input type="radio" name="sex" value="" checked><input type="radio" name="sex" value="">

checked属性表示,界面加载过程中默认选中一个。
界面如下:
在这里插入图片描述
后台输出如下:
在这里插入图片描述

多选
<input type="checkbox" name="fav" value="唱歌">
<input type="checkbox" name="fav" value="逛街">
<input type="checkbox" name="fav" value="游戏">

前台如下:

<!-- 多选 -->
        唱歌<input type="checkbox" name="hobby" value="唱歌">
        逛街<input type="checkbox" name="hobby" value="逛街">
        游戏<input type="checkbox" name="hobby" value="游戏">
        <hr>

页面如下:
在这里插入图片描述

后端如下:

private List<String> hobby;
    public List<String> getHobby() {
    
    
        return hobby;
    }

    public void setHobby(List<String> hobby) {
    
    
        this.hobby = hobby;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", id=" + id +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", hobby=" + hobby +
                '}';
    }

在这里插入图片描述

文件上传
<input type="file" name="avatar">

注意:上传文件必须加如下属性

    <form action="http://localhost:8077/test" method="post" enctype="multipart/form-data">
<!-- 文件上传 -->
        <input type="file" name="avatar">
   </form>

在这里插入图片描述
在这里插入图片描述

后台如下:

	private MultipartFile avatar;
    @Override
        public MultipartFile getAvatar() {
    
    
        return avatar;
    }

    public void setAvatar(MultipartFile avatar) {
    
    
        this.avatar = avatar;
    }
    
    public String toString() {
    
    
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", id=" + id +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", hobby=" + hobby +
                ", avatar=" + avatar.getSize() +
                '}';
    }

后台输出如下:
在这里插入图片描述

4. HTTP 请求

  • method 请求方式有
    • get (默认)提交时,数据跟在 URL 地址之后
    • post 提交时,数据在请求体内
  • enctype 在 post 请求时,指定请求体的数据格式
    • application/x-www-form-urlencoded(默认)
    • multipart/form-data
  • 其中表单项提供多种收集数据的方式
    • 有 name 属性的表单项数据,才会被发送给服务器

1) 请求组成

请求由三部分组成

  1. 请求行
  2. 请求头
  3. 请求体

请求行(必须)

红色标注
在这里插入图片描述
这里可以看到 GET /test2?name=tony HTTP/1.1 叫请求行
POST是请求方式
/test2?name=tony是URI
HTTP/1.1是协议版本

请求头(必须)

绿色标注
在这里插入图片描述
请求头:
格式:头名:头值
localhost:虚拟主机

请求体(非必须)

蓝色标注的,并不是请求都会携带
在这里插入图片描述
可以用 telnet 程序测试

2) 请求方式与数据格式

get 请求示例

GET /test2?name=%E5%BC%A0&age=20 HTTP/1.1
Host: localhost
  • %E5%BC%A0 是【张】经过 URL 编码后的结果

java后台这么写

 @RequestMapping("/test2")
    @ResponseBody
    public String test2(String name) {
    
    
        System.out.println("name:" + name);
        return "收到:" + name;
    }

这里我们这么发送

GET /test2?name=tony HTTP/1.1
Host: localhost

这里要用虚拟机安装telnet,这里我的是CentOS,指令如下:

yum install telnet -y

安装好后,用虚拟机连接windows本机

telnet ip 端口号

回车,连接上后,输入

GET /test2?name=tony HTTP/1.1
Host: localhost

敲击两次回车后,返回结果:
在这里插入图片描述

post 请求示例

POST /test2 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 21

name=%E5%BC%A0&age=18

application/x-www-form-urlencoed 格式细节:

  • 参数分成名字和值,中间用 = 分隔
  • 多个参数使用 & 进行分隔
  • 【张】等特殊字符需要用 encodeURIComponent() 编码为 【%E5%BC%A0】后才能发送

这里我们测试一下,请求如下:

POST /test2 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

name=zhang

这里Content-Length = 10不是随便定义的,根据name=zhang一个英文字符占一个长度,共10个字节。

linux收到响应
在这里插入图片描述

多个参数

post请求多参数情况

如果参数是多个呢,这里我们修改一下Java后端的代码

@RequestMapping("/test22")
    @ResponseBody
    public String test22(String name, Integer age) {
    
    
        System.out.println("name:" + name + ",age:" + age);
        return "收到name:" + name + ",age:" + age;
    }

telnet测试如下:

POST /test22 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 16

name=Lucy&age=18

输出如下:
在这里插入图片描述
这里Content-Length是多少,我们可以简单学习javascript
打开浏览器输入F12在控制台中输入:

"name=Lucy&age=18".length

回车后
在这里插入图片描述
这里16就是Content-Length的值。

get请求多参数情况

这里我们再尝试一下get请求多参数的情况

GET /test22?name=tony&age=20 HTTP/1.1
Host: localhost

返回如下:
在这里插入图片描述

汉字发送

在浏览器按F12,打开控制台

encodeURIComponent("张")

回车后,就出来了汉字的编码

'%E5%BC%A0'

我们再发送一下GET请求

GET /test22?name=%E5%BC%A0&age=20 HTTP/1.1
Host: localhost

收到结果:
在这里插入图片描述
那么“张”这个汉字,是怎么变成URL Code的呢?我们研究一下。
也就是说%E5%BC%A0是怎么来的。

我们不妨先用Java代码,把“张”转换成对应的UTF-8格式

@Test
    public void test() throws Exception {
    
    
        // utf-8
        byte[] bytes = "张".getBytes(StandardCharsets.UTF_8);
        System.out.println(bytes);

        for (byte b : bytes) {
    
    
            System.out.println(Integer.toHexString(Byte.toUnsignedInt(b)));
        }
    }

输出结果:

[B@50de0926
e5
bc
a0

json 请求示例

POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 25

{"name":"zhang","age":18}

json 对象格式

{"属性名":属性值}

其中属性值可以是

  • 字符串 “”
  • 数字
  • true, false
  • null
  • 对象
  • 数组

json 数组格式

[元素1, 元素2, ...]

我们发送如下:

POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 25

{"name":"zhang","age":18}

Java后台新建实体类

package com.itheima.pojo;

/**
 * @ClassName: Req
 * @Description:
 * @Author: wty
 * @Date: 2023/7/13
 */

public class Req {
    
    
    private String name;
    private Integer 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;
    }

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

Controller如下:
这里注意,接收json格式的数据,必须加上@RequestBody注解。

    @RequestMapping("/test3")
    @ResponseBody
    public Req test3(@RequestBody Req req) {
    
    
        System.out.println(req);
        return req;
    }

返回如下:
在这里插入图片描述
这里的19和0是分块发送的,最终字符数量是0,就停止发送。
这里发送的时候Content-Length: 25 明明是25
接收的时候怎么变成19

Java后台输出如下:
在这里插入图片描述
如果是JDK16,还可以使用Req,替代实体类,但是Req相当于get,初次赋值后不会更改,而没有set方法,是不可变的对象。

@RequestMapping("/test3")
    public Req test3(@RequestBody Req req) {
    
    
        System.out.println(req);
        return req;
    }
    
record Req(String name,Integer age){
    
    }

注意:json汉字可以照常发送

打开浏览器输入F12,之后输入

'{"name":"张","age":18}'.length

返回:21(字符)

因为传递是按照字节的,UTF-8中,一个汉字是三个字节,所以
21 -1 + 3 = 23
所以Content-Length是23

POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 23

{"name":"张","age":18}

结果如下:
在这里插入图片描述

multipart 请求示例

POST /test22 HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=123
Content-Length: 125

--123
Content-Disposition: form-data; name="name"

lisi
--123
Content-Disposition: form-data; name="age"

30
--123--
  • boundary=123 用来定义分隔符
  • 起始分隔符是 --分隔符
  • 结束分隔符是 --分隔符--

Java后端如下:

    @RequestMapping("/test22")
    @ResponseBody
    public String test22(String name, Integer age) {
    
    
        System.out.println("name:" + name + ",age:" + age);
        return "收到name:" + name + ",age:" + age;
    }

telnet结果如下:
在这里插入图片描述
这里有个疑问
Content-Length: 125是如何计算的

·--123
Content-Disposition: form-data; name="name"

lisi
--123
Content-Disposition: form-data; name="age"

30
--123--·.length

这里要引入反引号,最后答案是116
在这里插入图片描述
116相比于125少了9个字节,那这9个字节差在哪儿呢。就差在换行符上
在换行中其实有两个符号\r和\n,而JavaScript中只计算了一个,而我们的多行数据,刚好是9行,所以要加9

数据格式小结

客户端发送

  • 编码
    • application/x-www-form-urlencoded :url 编码
    • application/json:utf-8 编码
    • multipart/form-data:每部分编码可以不同
  • 表单只支持以 application/x-www-form-urlencoded 和 multipart/form-data 格式发送数据
  • 文件上传需要用 multipart/form-data 格式
  • js 代码可以支持任意格式发送数据

服务端接收

  • 对 application/x-www-form-urlencoded 和 multipart/form-data 格式的数据,Spring 接收方式是统一的,只需要用 java bean 的属性名对应请求参数名即可
  • 对于 applicaiton/json 格式的数据,Spring 接收需要使用 @RequestBody 注解 + java bean 的方式

3) session 原理

Http 无状态,有会话

  • 无状态是指,请求之间相互独立,第一次请求的数据,第二次请求不能重用
  • 有会话是指,客户端和服务端都有相应的技术,可以暂存数据,让数据在请求间共享

服务端使用了 session 技术来暂存数据

GET /s1?name=zhang HTTP/1.1
Host: localhost

Java后端如下:

@RequestMapping("/s1")
    @ResponseBody
    public String s1(HttpSession session, String name) {
    
    
        session.setAttribute("name", name);
        return "数据已存储";
    }

返回数据如下:
在这里插入图片描述
jsessionid = 5EC247CB0B301ACBFE90157D6F723CC5

GET /s2 HTTP/1.1
Host: localhost
Cookie: JSESSIONID=5EC247CB0B301ACBFE90157D6F723CC5

Java后端代码如下:

    @RequestMapping("/s2")
    @ResponseBody
    public String s2(HttpSession session) {
    
    
        return "取出数据" + session.getAttribute("name");
    }

输出结果如下:
在这里插入图片描述

session 技术实现身份验证

Client LoginController LoginInterceptor Session 登录请求 检查用户名,密码,验证通过 存入用户名 登录成功 其它请求 获取用户名 用户名存在,放行 Client LoginController LoginInterceptor Session

缺点:不适用于分布式项目,因为分布式项目有多台服务器,如果每一台都存储一份完整session,显然不太合理,这里引出了jwt技术。

4) jwt 原理

jwt 技术实现身份验证

Client LoginController LoginInterceptor 登录请求 检查用户名,密码,验证通过 登录成功,返回token 其它请求,携带token 校验token,校验无误,放行 Client LoginController LoginInterceptor

生成 token

GET /j1?name=zhang&pass=123 HTTP/1.1
Host: localhost

在这里插入图片描述
token为
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E

Java后端代码

    @RequestMapping("/j1")
    @ResponseBody
    public String j1(String name, String pass) {
    
    
        if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(pass)) {
    
    
            if ("zhang".equals(name) && "123".equals(pass)) {
    
    
                String token = Jwts.builder().setSubject(name).signWith(key).compact();
                return "验证身份通过:" + token;
            } else {
    
    
                return "验证身份失败";
            }
        }
        return "用户名或者密码为空,验证身份失败";
    }

校验 token

GET /j2 HTTP/1.1
Host: localhost
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E

Java后端代码

@RequestMapping("/j2")
    @ResponseBody
    public String j2(@RequestHeader String authorization) {
    
    
        try {
    
    
            System.out.println(authorization);
            Jws<Claims> jws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(authorization);
            return "校验通过, 你是:" + jws.getBody().getSubject();
        } catch (Exception e) {
    
    
            return "校验失败";
        }
    }

输出结果如下:
在这里插入图片描述

这里有个问题,就是这个长字符串的token有什么用,这里我们在IDEA中编写一个方法

 @Test
    public void test() {
    
    
        //              header(签名算法)       payload(数据)        签名
        //                                   eyJzdWIiOiJhZG1pbiJ9
        // token分成了3个部分,每个部分由.进行分割
        // 第一个部分 eyJhbGciOiJIUzI1NiJ9 header(签名算法)未加密
        // 第二个部分 eyJzdWIiOiJ6aGFuZyJ9 payload(数据)未加密
        // 第三个部分 qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E 签名 加密
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E";
        //               1                   2           3 ==> 6
        //               1                   4           3 ==> 8
        // 第一个部分
        System.out.println(new String(Base64.getDecoder().decode("eyJhbGciOiJIUzI1NiJ9")));
        // 第二个部分
        System.out.println(new String(Base64.getDecoder().decode("eyJzdWIiOiJ6aGFuZyJ9")));

        // 第三个部分
        // admin 权限大的用户 可以做所有的操作
        //String str = "{\"sub\":\"zhang\"}";
        // eyJzdWIiOiJ6aGFuZyJ9

        String str = "{\"sub\":\"admin\"}";
        // eyJzdWIiOiJhZG1pbiJ9
        System.out.println(Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8)));
    }

我们把中间的权限名称替换一下
替换成admin用户的

GET /j2 HTTP/1.1
Host: localhost
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E

我们访问试了下,发现没有骗过服务器
在这里插入图片描述
那为什么服务器能检验出问题呢,主要在第三部分签名上,签名是根据前两个部分生成了秘钥。

//       
//               1                   2           3 ==> 6
//               1                   4           3 ==> 8

5. CSS

即 Cascading Style Sheets,它描述了网页的表现与展示效果

1) 选择器

type 选择器 - 根据标签名进行匹配(元素选择器)

新建一个文件style-exercise.css

p {
    
    
    background-color: blueviolet;
}

html加上如下代码

<link rel="stylesheet" href="style-exercise.css">

效果如下:段落标签的背景颜色变为了紫色
在这里插入图片描述

class 选择器 - 根据元素的 class 属性进行匹配

修改html中需要修改的部分标签

<body>
    <p id="p1">1111111111111</p>
    <p id="p2" class="c2">2222222222222</p>
    <p id="p3" class="c3">3333333333333</p>

</body>

修改style-exercise.css

/* class选择器 */
.c2 {
    
    
    background-color: brown;
}

.c3 {
    
    
    background-color: chartreuse;
}

效果如下:
在这里插入图片描述

id 选择器 - 根据元素的 id 属性进行匹配

修改html文件

<body>
    <p id="p1">1111111111111</p>
    <p id="p2">2222222222222</p>
    <p id="p3">3333333333333</p>

</body>

修改style-exercise.css

/* id选择器 */
#p1 {
    
    
    background-color: cornflowerblue;
}

效果如下:
在这里插入图片描述
这里我们不禁要问,如果多个选择器都匹配到了同一个元素,那么优先级是怎么样的呢?
css代码如下:

/* 元素选择器-type */
p {
    
    
    background-color: blueviolet;
}

/* class选择器 */
.c2 {
    
    
    background-color: brown;
}

/* id选择器 */
#p2 {
    
    
    background-color: cornflowerblue;
}

我们都修改段落2的样式
最后效果如下:
在这里插入图片描述
结论:
id选择器 > class选择器 > 选择选择器

2) 属性和值

  • background-color : red;
  • display

详见
CSS样式查询

这里简要讲解一下display属性
代码如下:

#p2 {
    
    
    background-color: cornflowerblue;
    display: none;
}

页面如下:发现p2被隐藏了
在这里插入图片描述
如果更改为

/* id选择器 */
#p2 {
    
    
    background-color: cornflowerblue;
    display: block;
}

发现p2就展示出来了
在这里插入图片描述

3) 布局

与布局相关的 html 元素

div

资料提供的界面如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>布局</title>
    <style>
        html,body {
      
      
            margin:0;
            width: 100%;
            height: 100%;
            text-align: center;
            font-size: 30px;
            font-weight: bold;            
        }
        div{
      
      
            box-sizing: border-box;
        }
        .container {
      
      
            height: 100%;            
            position: relative;
        }
        #header {
      
      
            background-color:rgb(152, 152, 255);
            width: 100%;
            height: 80px;
            padding-top: 10px;
        }
        #aside {
      
      
            background-color:aquamarine;
            float: left;
            width: 200px;
            height: calc(100% - 140px);
            padding-top: 10px;
        }
        #main {
      
      
            background-color:honeydew;
            float: left;
            width: calc(100% - 200px);
            height: calc(100% - 140px);
            padding-top: 10px;
            padding-left: 20px;
            text-align: left;
        }
        #footer {
      
      
            background-color:darksalmon;
            height: 60px;
            padding-top: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div id="header">#header</div>
        <div id="aside">#aside</div>
        <div id="main">#main</div>
        <div style="clear: both;"></div>
        <div id="footer">#footer</div>
    </div>
    
</body>
</html>

效果如图:
在这里插入图片描述
这里style可以理解为内部样式,之前我们新建css,通过link引入,这个叫做外部样式

我们给div id ="main"中增加type类型的文本

<body>
    <div class="container">
        <div id="header">#header</div>
        <div id="aside">#aside</div>
        <div id="main">
            #main
            <form action="">
                <input type="text" value="文本">
            </form>
        </div>
        <div style="clear: both;"></div>
        <div id="footer">#footer</div>
    </div>

</body>

样式如下:增加了文本
在这里插入图片描述

template

先看下资料的源码

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模板</title>
    <style>
        html,
        body {
      
      
            margin: 0;
            width: 100%;
            height: 100%;
        }

        .btn {
      
      
            padding: 10px;
        }

        .out {
      
      
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            background-color:darkgrey;
        }

        .in {
      
      
            width: 200px;
            box-sizing: border-box;
            height: 200px;
            border: solid 2px black;
            padding: 10px;
            background-color: antiquewhite;
            margin: 10px;
            float: left;
        }
    </style>
</head>

<body>    
    <div class="out">
        <div class="btn">
            <input type="button" value="根据模板创建" id="add">
        </div>
        
    </div>
    <template id="t">
        <div class="in">
            <form action="">
                <p><label>姓名</label> <input type="text"></p>
                <p><label>年龄</label> <input type="text"></p>
                <p><input type="submit" value="添加"></p>
            </form>
        </div>
    </template>
    <script>
        document.getElementById("add").onclick = () => {
      
      
            let t = document.getElementById("t");
            let inputs = t.content.querySelectorAll("input");
            inputs[0].value = randomGenerator("abcdefghijklmnopqrstuvwxyz", 5);
            inputs[1].value = randomGenerator("1234567890", 2);
            const c = document.importNode(t.content, true);
            document.querySelector(".out").appendChild(c);
        }
        function randomGenerator(str, n) {
      
      
            const result = [];
            for (let i = 0; i < n; i++) {
      
      
                result.push(str.charAt(Math.floor(Math.random() * str.length)))
            }
            return result.join("");
        }
    </script>
</body>

</html>

效果如下:点击模版之后
在这里插入图片描述
通过JavaScript动态将姓名和年龄加入到了该布局中
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/sinat_38316216/article/details/131658170