HTTP协议、Java前后端交互、Servlet

news2024/11/19 1:31:10

文章目录

  • 抓包工具 Fiddler
  • HTTP 请求和响应结构
  • URL 唯一资源定位符
  • HTTP 协议中的方法
  • 请求报头(header)
  • HTTP响应
  • 构造 HTTP 请求
    • 基于 form 标签
    • 基于 ajax
    • 使用 Postman
  • HTTPS
    • 和 HTTP 的区别
    • 对称密钥和非对称密钥
    • 数字证书
  • Tomcat
  • Servlet
    • 创建 Maven 项目
    • 引入依赖
    • 创建目录
    • 编写代码
    • 打包
    • 部署和验证
    • 更快地部署:Smart Tomcat 插件
    • 常见错误总结
  • Servlet 详解
    • HttpServlet
    • HttpServletRequest
    • 构造和解析 json
    • HttpServletResponse
      • 例子:自动刷新
      • 例子:重定向
    • 例子:表白墙服务器
  • Cookie 和 Session
    • 简介
    • HttpServletRequest 类中的方法
    • HttpServletResponse 类中的相关方法
    • HttpSession 接口中的相关方法
    • 例子:用户登录
  • 上传文件
    • HttpServletRequest 相关方法
    • Part 类方法

抓包工具 Fiddler

Fiddler 是一款用于 Web 调试和网络流量分析的工具。它是一种代理服务器,可以捕获和检查从计算机到互联网之间的所有 HTTP 流量。Fiddler 可以帮助开发人员诊断问题、监视流量、修改请求和响应等。

前往官网下载安装即可使用:https://www.telerik.com/

这里下载 Classic 版,下载完成后点击 Tools->Options->HTTPS,将 4 个勾全部勾选上,使其可以抓 HTTPS 包

img

HTTP 请求和响应结构

随便访问一个页面,点开详细信息,查看 Raw

  1. 首行,请求/响应的第一行

    img

    HTTP/1.1 200 OK
    版本 状态码 状态码描述
    
  2. 请求/响应报头 header

    header 里面都是键值对,每个键值对占一行,键和值以 : 分隔

  3. 空行:

    是请求/响应报头(header)的结束标记
    报头里有多少个键值对(有多少行)是不确定的,所以以空行作为结束标记

  4. 请求/响应正文:

    不是所有的 HTTP 请求都有正文

URL 唯一资源定位符

img

URL 里面如果没有写端口,此时浏览器就会给一个默认值:http:// 默认端口 80,https:// 默认端口 443

查询字符串:请求发给服务器的时候带的参数

  • ? 作为查询字符串的起始标志,后面的内容就是查询字符串的本体
  • 通过键值对的方式来组织,键值对之间使用 & 来分割,键和值之间使用 = 来分割

路径:指定了服务器上资源的具体位置

片段标识符:起到页面内部跳转的效果

HTTP 协议中的方法

方法说明
GET获取资源
POST传输实体主体
PUT传输文件
HEAD获得报文首部
DELETE删除文件
OPTIONS询问支持的方法
TRACE追踪路径
CONNECT要求用隧道协议连接代理
LINK建立和资源之间的联系
UNLINE断开连接关系

触发 GET 请求的场景

  1. 浏览器地址栏输入一个 URL,回车
  2. HTML 标签,如 a,img,link,script
  3. form
  4. ajax

GET 的典型特点

  1. URL 的 query string 有时候有,有时候没有
  2. body 通常是空

POST 的特点

  1. URL 里通常没有 query string
  2. 通常有 body

GET 和 POST 的区别

  • 没有本质区别,它们之间可以相互替代
  • 在使用习惯上存在区别:
  • GET 主要用来获取数据,POST主要用来给服务器提交数据
  • GET 主要通过 query string 传递数据,POST 使用 body 传递数据
  • GET 请求一般建议实现成“幂等的”,POST 则不要求。幂等:多次输入的相同内容,得到的结果也相同,称为“幂等”
  • GET 一般是可以被缓存的,POST 不要求

请求报头(header)

Host:表示服务器主机的地址和端口

Content-Length:表示 body 中的数据长度

Content-Type:表示请求的 body 中的数据格式

常见的 HTTP 请求的 body 数据格式

  1. json
  2. urlencoded
  3. form-data

User-Agent(UA):描述使用设备

Referer:表示这个页面是从哪个页面跳转过来的

Cookie:是浏览器在本地存储数据的一种机制,每个网站分配的cookie独立,内部存储的是键值对

HTTP响应

状态码

以下是一些常见的 HTTP 响应状态码:

  1. 1xx(信息性状态码):
    • 100 Continue:服务器已经收到请求的头部,并且客户端应继续发送请求的其余部分。
  2. 2xx(成功状态码):
    • 200 OK:请求成功,服务器已经成功处理了请求。
    • 201 Created:请求已经被实现,而且有一个新的资源已经依据请求的需要而建立。
  3. 3xx(重定向状态码):
    • 301 Moved Permanently:被请求的资源已被永久移动到新位置。
    • 302 Found:请求的资源现在临时从不同的 URI 响应请求。
    • 304 Not Modified:如果客户端的缓存是最新的,则返回此状态码。
  4. 4xx(客户端错误状态码):
    • 400 Bad Request:服务器无法理解请求的语法。
    • 401 Unauthorized:请求要求身份验证。
    • 403 Forbidden:服务器理解请求,但拒绝执行。
    • 404 Not Found:服务器无法找到请求的资源。
    • 405 请求的方法不支持
  5. 5xx(服务器错误状态码):
    • 500 Internal Server Error:服务器遇到不可预知的情况。
    • 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到无效响应。
    • 503 Service Unavailable:服务器目前无法处理请求。
    • 504 超时

Content-Type

Content-Type 是 HTTP 响应报头之一,用于指示响应体的媒体类型(Media Type)或者内容类型。它告诉客户端如何解析响应体的数据。以下是一些常见的 Content-Type 值及其含义:

  1. text/plain: 纯文本,不包含任何格式的样式或标记。
  2. text/html: HTML 文档,用于网页内容。
  3. text/css: CSS 样式表,用于定义文档的样式和布局。
  4. text/javascript: JavaScript 脚本。
  5. application/json: JSON 格式,用于在客户端和服务器之间传输数据。
  6. application/xml: XML 格式,用于表示结构化的数据。
  7. image/jpeg、image/png、image/gif: 分别表示 JPEG、PNG、GIF 格式的图像。
  8. application/pdf: 表示 Adobe Portable Document Format(PDF)文件。
  9. application/octet-stream: 二进制流数据,通常用于传输不属于以上任何类型的二进制文件。
  10. multipart/form-data: 用于在 HTML 表单中上传文件。

构造 HTTP 请求

使用代码构造,主要是两种方式

基于 form 标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GET 请求表单</title>
</head>
<body>

    <form action="/example" method="get">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name" required>
        
        <label for="age">Age:</label>
        <input type="number" id="age" name="age" required>
        
        <button type="submit">Submit</button>
    </form>

</body>
</html>

  • action="/example" 指定了表单提交的目标 URL。
  • method="get" 指定了使用 GET 请求。
  • 每个输入字段都有一个 name 属性,这些属性的值将作为参数发送到服务器。

当用户填写表单并点击 “Submit” 按钮时,浏览器将构造一个类似于以下的 URL:

/example?name=zhangsan&age=18

method="get" 改为 method="post",请求方法改为 post

表单数据将被包含在请求的消息体中,而不是作为 URL 的一部分。

注意:form 只支持 get 和 post

基于 ajax

Ajax(Asynchronous JavaScript and XML)是一种用于在 Web 页面中进行异步数据交换的技术。它允许在不刷新整个页面的情况下,通过后台与服务器进行数据交互,获取或发送数据。Ajax 的核心是通过 JavaScript 和 XML(尽管现在更常用 JSON)来实现异步通信。

jQuery 对 ajax 进行了封装,方便使用

首先引入 jQuery ,搜索 jQuery cdn,选择 minified,将 j s代码粘贴到本地,或者直接将远程 cdn 服务器上的js文件url引入。

$.ajax();
// jquery 构造 ajax 请求的核心函数
// $ 是一个变量名,是 jquery 中的一个特殊对象
// jquery 里面提供的各种 API 都是以 $ 对象的方法的方式来体现的

使用对象作为参数,例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery AJAX 示例</title>
    <!-- 引入 jQuery 库 -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>

    <div id="result"></div>

    <script>
        // 使用 jQuery 发起 AJAX 请求
        $.ajax({
            url: 'https://jsonplaceholder.typicode.com/posts/1', // 服务器端 API 地址
            type: 'GET', // 请求类型
            dataType: 'json', // 预期的数据类型
            success: function(data) {
                // 请求成功时的处理
                // 在这个例子中,我们将返回的数据显示在页面上
                $('#result').html('<p>Title: ' + data.title + '</p><p>Body: ' + data.body + '</p>');
            },
            error: function(xhr, status, error) {
                // 请求失败时的处理
                console.error('Error: ' + status + ', ' + error);
            }
        });
    </script>

</body>
</html>

使用 Postman

HTTPS

和 HTTP 的区别

  1. 安全性:
    • HTTP: HTTP 是一种不加密的协议,数据在传输过程中是以明文形式传送的。这意味着如果攻击者能够截获网络通信,他们可以轻松地读取或修改传输的数据。因此,HTTP 不适合传输敏感信息,如登录凭证、支付信息等。
    • HTTPS: HTTPS 使用了 TLS/SSL 协议进行加密,确保数据在传输过程中是加密的。这使得通过 HTTPS 传输的数据更难被窃取或篡改。HTTPS 是一种更安全的协议,适用于需要保护隐私和安全性的场景,如在线支付、登录等。
  2. 协议标识:
    • HTTP: 使用标准的 “http://” URL 标识,通常在浏览器中显示为 “http://”.
    • HTTPS: 使用标准的 “https://” URL 标识,通常在浏览器中显示为 “https://”. 还可能会显示一个锁图标,表示连接是安全的。
  3. 端口:
    • HTTP: 默认使用端口 80。
    • HTTPS: 默认使用端口 443。
  4. 证书:
    • HTTP: 不需要使用数字证书。
    • HTTPS: 需要使用由可信任的证书颁发机构(CA)颁发的数字证书。这个证书用于验证服务器的身份,确保连接是安全的。
  5. 性能:
    • HTTP: 由于不涉及加密解密过程,通常比 HTTPS 稍微快一些。
    • HTTPS: 由于数据加密和解密的额外开销,可能会稍微减缓数据传输速度。

对称密钥和非对称密钥

对称密钥和非对称密钥是两种常见的加密算法使用的密钥体制,它们分别用于加密和解密信息。

  1. 对称密钥(Symmetric Key):
    • 定义: 对称密钥是一种使用相同密钥进行加密和解密的加密算法。加密和解密都使用相同的密钥,这就是为什么它被称为“对称”的原因。
    • 特点: 算法简单、加解密速度快,但密钥的安全分发是一个挑战。因为发送方和接收方都必须事先共享相同的密钥,传输过程中,密钥也可能被黑客截获,存在安全风险。
    • 示例: 常见的对称加密算法包括 DES(Data Encryption Standard)、AES(Advanced Encryption Standard)等。
  2. 非对称密钥(Asymmetric Key):
    • 定义: 非对称密钥使用一对密钥,分别是公钥和私钥。公钥用于加密信息,而私钥用于解密信息。这使得公钥可以安全地发布,而私钥则必须保持机密。
    • 特点: 相对于对称密钥,非对称密钥更安全,但由于其复杂性,加解密速度较慢。非对称密钥通常用于安全通信的密钥交换阶段。
    • 示例: 常见的非对称加密算法包括 RSA(Rivest-Shamir-Adleman)、ECC(Elliptic Curve Cryptography)等。

数字证书

数字证书是一种用于验证网络通信中身份的安全工具,通常用于建立加密通信。数字证书是由称为证书颁发机构(Certificate Authority,简称CA)的可信任实体签发的一种电子文档,用于确认某个实体(通常是一个网络服务器)的身份。

数字证书包含了以下关键信息:

  1. 公钥: 数字证书包含一个公钥,用于加密和解密信息。公钥是一个用于加密数据的密码学密钥,同时也用于验证由该证书签发者签名的数据。
  2. 身份信息: 数字证书通常包含与公钥相关联的实体的身份信息,如域名(对于服务器证书)或个人信息(对于个人证书)。这个信息被称为主题(Subject)。
  3. 数字签名: 证书颁发机构使用其私钥对证书的内容进行数字签名,以确保证书的完整性和真实性。这个数字签名可以被其他人使用 CA 的公钥进行验证。

在网络通信中,数字证书的传输和验证通常涉及以下步骤:

  1. 握手阶段: 在建立安全连接(如HTTPS)时,通信的两端(客户端和服务器)会执行握手阶段。在这个阶段,双方协商加密算法、生成会话密钥等。
  2. 服务器发送证书: 在握手过程中,服务器将其数字证书发送给客户端。这个证书包含了服务器的公钥、服务器的身份信息(主题)、证书颁发机构的签名以及其他相关信息。
  3. 客户端验证证书: 客户端收到服务器的证书后,会验证证书的有效性。这个验证过程包括以下步骤:
    • 验证数字签名: 客户端使用证书颁发机构的公钥验证证书的数字签名,确保证书的完整性和真实性。
    • 验证有效期: 客户端检查证书的有效期,确保证书在当前时间内有效。
    • 验证域名: 对于服务器证书,客户端会检查证书中的域名信息与实际连接的域名是否匹配,以防止钓鱼攻击。
  4. 生成共享密钥: 如果证书验证通过,客户端使用服务器的公钥加密一个随机生成的会话密钥,并将其发送给服务器。
  5. 服务器解密会话密钥: 服务器使用其私钥解密客户端发送的会话密钥,从而两端都获得了相同的会话密钥。
  6. 加密通信: 之后的通信将使用共享的会话密钥进行加密和解密,保障通信的保密性和完整性。

Tomcat

Apache Tomcat 是一个轻量级的开源 Java 应用服务器,用于托管和运行 Java Web 应用。

官方网站:https://tomcat.apache.org/

下载:这里选择 Tomcat 8.5 的版本

img

解压好进入 bin 目录,执行 startup 来启动服务器。如果启动失败,可以尝试在 cmd 中打开,会返回报错信息。

启动成功,打开浏览器访问 http://127.0.0.1:8080/ 可以看到 Tomcat 自带的页面

webapps 目录下存放我们自己写的 html 页面。

url 请求根目录为 webapps ,其中的 ROOT 是一个特殊的目录,如果请求的是 ROOT 里的页面,不用在 url 中加 ROOT

Servlet

Servlet 是一种用于在服务器端处理客户端请求的 Java 编程接口(API)。Servlets 是 Java 语言编写的服务器端程序,主要用于创建动态的网站。它们可以接收来自客户端的请求、处理请求并生成响应。

创建 Maven 项目

Apache Maven(通常称为 Maven)是一个用于管理项目构建、依赖关系和文档的开源项目管理工具。

IDEA 内置 Maven,创建项目时选上即可。

一个 Maven 项目的目录:

img

main 用于存放业务代码,test 用于存放测试代码

java 用于存放 java 代码,resources 存放项目中依赖的图片、音频、字体等资源文件

pom.xml 是 Maven 项目的核心文件,描述了这个项目的属性信息

引入依赖

去 Maven Repository 搜索 servlet,选择 3.1.0 版本

img

将方框内的代码复制粘贴到 pom.xml 里面,具体步骤是在<project> 标签内创建 <dependencies> 标签,将代码粘贴到<dependencies> 内。

复制进去,点击刷新按钮,就会自动开始下载了。

默认的下载位置是家目录下的 .m2 文件中

创建目录

为了我们的程序能被 Tomcat 识别,需要创建如下的目录结构

img

在 main 目录下创建 webapp 目录,在其中创建 WEB-INF 目录,在其中创建 web.xml 文件

web.xml 中复制如下内容:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

html 文件就是存放在 webapp 目录下

编写代码

接下来我们可以在 java 目录中创建类了

创建一个类,继承 HttpServlet 并重写 doGet 方法。

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Hello world"); // 打印到服务器的控制台
        resp.getWriter().write("Hello world"); // 打印到响应报文,显示在页面上
    }
}

  • Tomcat 会自动识别合适的时机来调用 doGet 方法(合适的时机比较复杂,但可以肯定一定是通过 GET 请求触发),参数 req 和 resp 包含了 HTTP 请求响应的信息。

    回顾一下网络编程的步骤:

    1. 读取请求并解析
    2. 根据请求计算响应
    3. 将响应返回给客户端

    其中的1、3两步都是由 Tomcat 自动完成,第 2 步由我们自己重写的 doGet 方法完成

  • @WebServlet("/hello") 注解,用来约定 HTTP 请求的 url 是什么样的 path 才会调用到当前这个 Servlet 类

打包

点开右边的 m 图标,展开 Lifecycle,双击 package 或者右键 package 运行。

img

接下来它就会执行从 clean 到 package 的过程

在 target 目录下可以找到最终生成的 jar 包

img

然而 jar 包并不能被 Tomcat 识别,Tomcat 的可以识别的是 war 包,需要到 pom.xml 中修改配置,让它生成 war 包:

<project> 标签里添加 :

<packaging>war</packaging>
<build>
    <finalName>hello_servlet</finalName>
</build>

<packaging> 标签指定了生成 war 包,<build> 中的 <finalName> 标签指定生成的包的名字

重新双击 package,打包操作完成。

部署和验证

把刚才得到的 war 包,拷贝到 Tomcat 的 webapps 目录中

然后启动 Tomcat,启动时会自动对我们拷贝的 war 包进行解压,生成文件夹。

打开浏览器,指定好路径,进行验证:

img

/hello_servlet/hello 路径:

  • 其中 /hello_servlet 是 webapps 里的目录,又叫上下文路径。
  • /hello 是 @WebServlet("/hello") 注解里的路径,又叫 Servlet 路径

更快地部署:Smart Tomcat 插件

Smart Tomcat 插件可以帮我们省略打包、部署和启动 Tomcat 的过程。

安装好 Smart Tomcat 插件后,进入 Edit Configurations…

img

点 + 号,选择 Smart Tomcat

img

配置 Tomcat 路径,其他保持默认

img

点击 OK 之后,右上角出现配置好的Smart Tomcat,点击绿色三角按钮即可自动启动 Tomcat 并完成部署。

使用 Smart Tomcat 插件,Context path 就是在这个页面里面设置的。

常见错误总结

  1. 404 大概率是 url 写错,也有可能是 webapp 没有加载正确(比如 web.xml 写错了)
  2. 405 可能收到 GET 请求,但是你没有实现 doGet。或者写了 doGet,但是没有把 super.doGet 这一行删掉
  3. 500 服务器内部错误,比如你的代码抛异常了
  4. 返回空白页面,可能是忘记填写 resp
  5. 无法访问此网站,可能是 Tomcat 没有正确启动

Servlet 详解

HttpServlet

方法调用时机
initHttpServlet 实例化后被调用一次
destroyHttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求时调用
doGet收到 GET 请求时调用(由 service 方法调用)
doPost收到 POST 请求时调用(由 service 方法调用)
doPut/doDelete/doOptions/...收到其他请求时调用(由 service 方法调用)

HttpServlet 的生命周期

  1. 首次使用,先调用一次 init
  2. 每次收到请求,调用 service,在 service 内部通过方法来决定调用哪个 doXXX
  3. 销毁之前调用 destroy

HttpServletRequest

方法说明
String getProtocol()返回请求使用的协议的名称和版本
String getMethod()返回请求的HTTP方法的名称
String getRequestURI()返回此请求的URL的一部分
String getContextPath()返回请求URI中指示请求上下文的部分
String getQueryString()返回包含在请求URL的路径后的查询字符串
Enumeration<String> getParameterNames()返回包含参数名称的 String 对象的枚举
String getParameter(String name)以字符串形式返回请求参数的值,如果该参数不存在,则返回null
String[] getParameterValues(String name)返回一个字符串数组,包含给定请求参数的所有值,如果该参数不存在,则返回null
Enumeration<String> getHeaderNames()返回此请求包含的所有标头名称的枚举。如果请求没有标头,则此方法返回一个空枚举。
String getHeader(String name)以字符串形式返回指定请求头的值
String getCharacterEncoding()返回此请求正文中使用的字符编码的名称
String getContentType()返回请求正文的 MIME 类型,如果类型未知,则返回 null
int getContentLength()返回请求主体的长度(以字节为单位),并由输入流提供;如果长度未知,则返回-1
ServletInputStream getInputStream()使用 ServletInputStream 将请求的正文检索为二进制数据

例子:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(req.getProtocol());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getMethod());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getRequestURI());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getContextPath());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getQueryString());
        stringBuilder.append("<br>");

        Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            stringBuilder.append(name).append(": ").append(req.getHeader(name)).append("<br>");
        }
        resp.setContentType("text/html");
        resp.getWriter().write(stringBuilder.toString());
    }
}

结果:

img

回显 query string:

@WebServlet("/studentInfo")
public class StudentInfoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String queryString = req.getQueryString();
        resp.getWriter().write(queryString);
        String classId = req.getParameter("classId");
        String studentId = req.getParameter("studentId");
        resp.getWriter().write("classId: " + classId + "studentId: " + studentId);
    }
}

img

对 POST 请求获取 body 中的参数,使用的依然是 getParameter 方法:

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf8");
        // 获取 body 中的参数
        // 约定使用 application/x-www-form-urlencoded 格式传参
        // 这个格式和 query string 相同,只是数据在 body 中
        String classId = req.getParameter("classId");
        String studentId = req.getParameter("studentId");
        resp.getWriter().write("classId: " + classId + "studentId: " + studentId);
    }

使用 Postman 来构造 POST 请求,获取返回结果

img

构造和解析 json

处理 json 需要用到第三方库,这里使用 Jackson,去 Maven 仓库搜索然后引入自己的项目中就可以了。

处理 Json 的核心只有两个方法:

  • 将 Json 转换为 Java 对象——readValue
  • 将 Java 对象转换为 Json——writeValue

解析 json:

转换成的 Java 对象是什么类,需要事先定义:

class Student {
    public int classId;
    public int studentId;
}

注意:属性名要和 json 中的 key 一致

然后将使用 readValue 将 json 转换成 Java 对象

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    // 从请求的 body 中进行读取并解析
    // 使用 readValue 把 json 字符串转成 Java 对象
    Student student = objectMapper.readValue(req.getInputStream(), Student.class);
    resp.getWriter().write(student.classId + ", " + student.studentId);
}

使用 Postman 构造 Json 格式的 body,可以看到结果正确返回

img

HttpServletResponse

方法说明
void setStatus(int sc)设置此响应的状态码
void setHeader(String name, String value)设置具有给定名称和值的响应 header. 如果已经设置了 header,则新值将覆盖上一个值
void addHeader(String name, String value)添加具有给定名称和值的响应 header. 此方法允许响应标头具有多个值
void setContentType(String type)如果尚未提交响应,则设置发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)设置发送到客户端的响应的字符编码(MIME字符集)
void sendRedirect(String location)使用指定的重定向位置 URL 向客户端发送临时重定向响应并清除缓冲区
PrintWriter getWriter()返回一个PrintWriter对象,该对象可以向客户端发送字符文本
ServletOutputStream getOutputStream()返回适用于在响应中写入二进制数据的 Servlet 输出流。

例子:自动刷新

设置 header 中的 refresh 和刷新时间,可以让浏览器自动刷新

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh", "1");
        resp.getWriter().write(System.currentTimeMillis() + "");
    }
}

每刷新一次就会请求新的时间戳

例子:重定向

设置状态码为 3xx(典型的是302)

给 header 里设置一个 Location,表示跳转到的页面

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(302);
        resp.setHeader("Location", "https://www.baidu.com/");
    }
}

更简单的写法:

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("https://www.baidu.com/");
    }
}

例子:表白墙服务器

实现表白墙服务器,并且提交的数据可以持久化到数据库中。

前端页面:

<!DOCTYPE html>
<html lang="en">
<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>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }

        .container {
            width: 800px;
            margin: 10px auto;
        }

        .container h2 {
            text-align: center;
            margin: 30px 0px;
        }

        .row {
            height: 50px;
            display: flex;
            justify-content: center;
            margin-top: 5px;
            line-height: 50px;
        }

        .row span {
            height: 50px;
            width: 100px;
            line-height: 50px;
        }

        .row input {
            height: 50px;
            width: 300px;
            line-height: 50px;
        }

        .row button {
            width: 400px;
            height: 50px;
            color: white;
            background-color: orange;
            border: none;
            border-radius: 10px;
        }

        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <!-- 这是一个顶层容器, 放其他元素 -->
    <div class="container">
        <h2>表白墙</h2>
        <div class="row">
            <span></span>
            <input type="text" id="from">
        </div>

        <div class="row">
            <span>对谁</span>
            <input type="text" id="to">
        </div>

        <div class="row">
            <span>说什么</span>
            <input type="text" id="message">
        </div>

        <div class="row">
            <button>提交</button>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
        let container = document.querySelector('.container');
        let fromInput = document.querySelector('#from');
        let toInput = document.querySelector('#to');
        let messageInput = document.querySelector('#message');
        let button = document.querySelector('button');
        button.onclick = function() {
            // 1. 把用户输入的内容获取到. 
            let from = fromInput.value;
            let to = toInput.value;
            let message = messageInput.value;
            if (from == '' || to == '' || message == '') {
                return;
            }
            // 2. 构造一个 div, 把这个 div 插入到 .container 的末尾
            let newDiv = document.createElement('div');
            newDiv.className = 'row';
            newDiv.innerHTML = from + " 对 " + to + " 说: " + message;
            // 3. 把 div 挂在 container 里面
            container.appendChild(newDiv);
            // 4. 把之前的输入框内容进行清空
            fromInput.value = '';
            toInput.value = '';
            messageInput.value = '';

            // 5. 把输入框里取到的数据, 构造成 POST 请求, 交给后端服务器!
            let messageJson = {
                "from": from,
                "to": to,
                "message": message
            };
            $.ajax({
                type: 'post',
                // 相对路径的写法
                url: 'message',
                contentType: 'application/json;charset=utf8',
                // 绝对路径的写法
                // url: '/MessageWall/message',
                data: JSON.stringify(messageJson),
                success: function(body) {
                    alert("提交成功!");
                },
                error: function() {
                    // 会在服务器返回的状态码不是 2xx 的时候触发这个 error. 
                    alert("提交失败!");
                }
            });
        }

        // 这个函数在页面加载的时候调用. 通过这个函数从服务器获取到当前的消息列表. 
        // 并且显示到页面上. 
        function load() {
            $.ajax({
                type: 'get',
                url: 'message',
                success: function(body) {
                    // 此处得到的 body 已经是一个 js 对象的数组了. 
                    // ajax 自动帮我们进行类型转换了. 
                    // 遍历数组的元素, 把内容构造到页面上. 
                    let container = document.querySelector('.container');
                    for (let message of body) {
                        let newDiv = document.createElement('div');
                        newDiv.className = 'row';
                        newDiv.innerHTML = message.from + " 对 " + message.to + " 说 " + message.message;
                        container.appendChild(newDiv);
                    }
                }
            })
        }

        // 函数调用写在这里, 就表示在页面加载的时候来进行执行. 
        load();

    </script>
</body>
</html>

对 JDBC 的封装:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

// 期望通过这个类来完成数据库建立连接的过程.
// 建立连接需要使用 DataSource . 并且一个程序有一个 DataSource 实例即可. 此处就使用单例模式来实现.
public class DBUtil {
    private static DataSource dataSource = null;

    private static DataSource getDataSource() {
        if (dataSource == null) {
            dataSource = new MysqlDataSource();
            ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/MessageWall?characterEncoding=utf8&useSSL=false");
            ((MysqlDataSource)dataSource).setUser("root");
            ((MysqlDataSource)dataSource).setPassword("2222");
        }
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }

    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        // 此处还是推荐大家写成分开的 try catch.
        // 保证及时一个地方 close 异常了, 不会影响到其他的 close 的执行.
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

后端 java 代码:

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

// 对应到前端传来的请求的 body 格式.
// 此处要保证, 每个属性的名字都和 json 里的 key 对应 (顺序可以不一样)
// 同时也要保证这几个属性是 public 或者提供 public 的 getter 方法
class Message {
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    // 由于 ObjectMapper 会在多个方法中使用, 就提出来, 作为成员变量
    private ObjectMapper objectMapper = new ObjectMapper();

    // 负责实现客户端提交数据给服务器.
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 把 body 的 json 数据解析出来.
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        // 2. 把这个对象保存起来.
        save(message);
        System.out.println("message: " + message);
        // 3. 返回保存成功的响应
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write("{ \"ok\": 1 }");
    }

    // 负责实现客户端从服务器拿到数据.
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 由于约定的请求, 没有参数, 不需要进行任何解析操作
        resp.setContentType("application/json;charset=utf8");
        // 把对象转成 json 格式的字符串. 此处 messageList 是一个 List, 直接就被转成 json 数组了.
        List<Message> messageList = load();
        String respString = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respString);
    }

    // 把当前的消息存到数据库中
    private void save(Message message) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            // 1. 和数据库建立连接
            connection = DBUtil.getConnection();
            // 2. 构造 SQL 语句
            String sql = "insert into message values(?, ?, ?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, message.from);
            statement.setString(2, message.to);
            statement.setString(3, message.message);
            // 3. 执行 SQL 语句
            int ret = statement.executeUpdate();
            if (ret != 1) {
                System.out.println("插入失败!");
            } else {
                System.out.println("插入成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭连接.
            DBUtil.close(connection, statement, null);
        }
    }

    // 从数据库查询到记录
    private List<Message> load() {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<Message> messageList = new ArrayList<>();
        try {
            // 1. 建立连接
            connection = DBUtil.getConnection();
            // 2. 构造 SQL
            String sql = "select * from message";
            statement = connection.prepareStatement(sql);
            // 3. 执行 SQL
            resultSet = statement.executeQuery();
            // 4. 遍历结果集
            while (resultSet.next()) {
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message = resultSet.getString("message");
                messageList.add(message);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 5. 释放资源
            DBUtil.close(connection, statement, resultSet);
        }
        return messageList;
    }
}

Cookie 和 Session

简介

Cookie 经典应用场景就是用户登录,我们访问很多网站都只要第一次输入一下用户名密码,而后续访问这个网站一进去就已经是登录状态,这就用到了 Cookie

Cookie 使用键值对来保存信息,键是服务器自动生成的一串唯一的字符串,叫做 sessionId,值就是用户的详细信息。服务器只需要把键通过 Set-Cookie 返回给浏览器,就可以验证用户身份。

流程如图:

img

  • cookie 存在客户端,session 存在服务端,sessionId 在客户端和服务端都有

HttpServletRequest 类中的方法

方法说明
HttpSession getSession()返回与此请求关联的当前会话,如果请求没有会话,则创建一个会话
HttpSession getSession(boolean create)返回与此请求关联的当前会话,如果没有当前会话并且 createtrue,则返回新会话,如果没有当前会话并且 createfalse,则返回 null
Cookie[] getCookies()返回一个数组,该数组包含客户端随此请求发送的所有 Cookie 对象。如果未发送 cookie,此方法将返回 null

HttpServletResponse 类中的相关方法

方法说明
void addCookie(Cookie cookie)将指定的 cookie 添加到响应中。可以多次调用此方法来设置多个 cookie

HttpSession 接口中的相关方法

方法说明
void setAttribute(String name, Object value)使用指定的名称将对象绑定到此会话。如果具有相同名称的对象已绑定到会话,则会替换该对象
Object getAttribute(String name)返回此会话中与指定名称绑定的对象,如果该名称下未绑定任何对象,则返回 null

这两个方法一个是设置键值对,一个是根据键获取对应的值

例子:用户登录

包含一个登录页和一个主页,登录页可以输入用户名和密码,服务器验证正确性,如果正确就跳转到主页,主页显示当前用户的身份信息,并且显示出当前用户登录后的访问次数。实现用户登录一次后,不必重新登录。

一个简单的登录页面 login.html:用 form 构造 post 请求向服务器提交数据

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>登录页面</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="提交">
    </form>
</body>
</html>
package login;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

// 处理登录请求
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    // 读取请求中的参数,判定当前用户的身份信息是否正确
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html; charset=utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
            // 参数不正确
            resp.sendRedirect("用户名或密码不完整,登录失败");
            return;
        }
        // 验证用户名密码正确性
        if (!username.equals("zhangsan") || !password.equals("123")) {
            // 登录失败
            resp.getWriter().write("用户名或密码错误,登录失败");
            return;
        }
        // 登录成功
        // 创建会话,把用户信息填入 session
        HttpSession session = req.getSession();
        session.setAttribute("username", "zhangsan");

        Integer visitCount = (Integer) session.getAttribute("visitCount");
        if (visitCount == null) {
            session.setAttribute("visitCount", 0);
        }
        // 这里的页面跳转是一个 GET 请求
        resp.sendRedirect("index");
    }
}
package login;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 将当前的用户信息展示到页面上
        HttpSession session = req.getSession(false);
        if (session == null) {
            // 没有对应的session,重新登录
            resp.sendRedirect("login.html");
            return;
        }
        // 登录
        String username = (String) session.getAttribute("username");
        Integer visitCount = (Integer) session.getAttribute("visitCount");
        ++visitCount;
        session.setAttribute("visitCount", visitCount);

        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("当前用户为" + username + "访问次数" + visitCount);
    }
}

使用 Fiddler 抓包查看过程:

第一次请求:首次访问 login.html 页面:

GET http://127.0.0.1:8080/hello_servlet/login.html HTTP/1.1

请求里面没有cookie

HTTP/1.1 304

响应也没有 Set-Cookie。

第二次请求:输入用户名密码,提交:

POST http://127.0.0.1:8080/hello_servlet/login HTTP/1.1

可以看到确实是 POST 请求

username=zhangsan&password=123

请求 body 部分。

这个请求中也没有 Cookie。

HTTP/1.1 302
Set-Cookie: JSESSIONID=92263EBADDECE1E20FADE869B7715301; Path=/hello_servlet; HttpOnly

响应中出现了 Set-Cookie,JSESSIONID 是 Servlet 自动生成的 key,= 后面的就是 sessionId 了

第三次请求:重定向到主页

GET http://127.0.0.1:8080/hello_servlet/index HTTP/1.1
...
...
Cookie: JSESSIONID=92263EBADDECE1E20FADE869B7715301

这一次请求,有 Cookie 了,后续反复请求,都会带有这个 Cookie 了

上传文件

HttpServletRequest 相关方法

方法说明
Part getPart(String name)获取请求中具有给定名称的 Part
Collection<Part> getParts()获取此请求的所有 Part 组件,前提是该请求的类型为multipart/form-data。

Part 类方法

方法说明
String getSubmittedFileName()获取由客户端指定的文件名
String getContentType()获取此 part 的内容类型
long getSize()返回此文件的大小
void write(String fileName)将此上传项目写入磁盘的一种方便方法

例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="upload" method="post" enctype="multipart/form-data">
        <input type="file" name="MyFile">
        <input type="submit" value="上传">
    </form>
</body>
</html>

注意:一定要写 enctype="multipart/form-data" 才能上传文件

package upload;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Part part = req.getPart("MyFile");
        System.out.println(part.getSubmittedFileName());
        System.out.println(part.getSize());
        System.out.println(part.getContentType());
        part.write("d:/result.jpg");
        resp.getWriter().write("upload ok");
    }
}

注意:一定要加 @MultipartConfig 注解

结果:

网页显示:

upload ok

控制台输出:

刺客信条:英灵殿2022-9-16-0-46-30.jpg
477980
image/jpeg

查看 D 盘,确实有刚刚上传的 result.jpg,查看也确实是上传的图片

抓包查看上传文件时的 HTTP 请求:

POST http://localhost:8080/hello_servlet/upload HTTP/1.1
......
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBSLCs05c8ots3YZN
......

------WebKitFormBoundaryBSLCs05c8ots3YZN
Content-Disposition: form-data; name="MyFile"; filename="刺客信条:英灵殿2022-9-16-0-46-30.jpg"
Content-Type: image/jpeg

...图片内容,在这里显示是乱码...
------WebKitFormBoundaryBSLCs05c8ots3YZN--
  • 可以看到确实是 POST 请求,请求的 url 也没问题。
  • 在 Content-Type 里可以看到类型是 multipart/form-data,boundary 是边界,表示上传文件的起始和结束位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1276104.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSM框架(四):SSM整合 案例 + 异常处理器 +拦截器

文章目录 一、整合流程图1.1 Spring整合Mybatis1.2 Spring整合SpringMVC 二、表现层数据封装2.1 问题引出2.2 统一返回结果数据格式 代码设计 三、异常处理器3.1 概述3.2 异常处理方案 四、前端五、拦截器5.1 概念5.2 入门案例5.3 拦截器参数5.4 拦截器链 一、整合流程图 1.1 S…

2.qml 3D-View3D类学习

本章我们来学习View3D类。 View3D是用来渲染3D场景并显示在2D平面的类&#xff0c;并且该类可以放在QML2D下继承于Item子类的任何场景中&#xff0c;比如将View3D放在Rectangle中: Rectangle {width: 200 height: 200color: "red"View3D { anchors.fill: parent…

STM32CubeIDE(CUBE-MX hal库)----蓝牙模块HC-05(详细配置)

系列文章目录 STM32CubeIDE(CUBE-MX hal库)----初尝点亮小灯 STM32CubeIDE(CUBE-MX hal库)----按键控制 STM32CubeIDE(CUBE-MX hal库)----串口通信 STM32CubeIDE(CUBE-MX hal库)----定时器 文章目录 系列文章目录前言一、蓝牙配置二、CUBE-MX可视化配置三、蓝牙APP调试助手四、…

mysql在linux环境下安装(rpm)以及初始化后的登录配置

注&#xff1a;该安装步骤转载于CSDN,下方配置为原创 按照图片安装并初始化完成MySQL等操作后进行&#xff1b; 安装对于rpm包集合 1-查看安装情况&#xff08;有4个路径&#xff09; whereis mysql 2-查看服务状态 systemctl status mysql 3-初始化数据库 mysqld --initial…

6.5 Windows驱动开发:内核枚举PspCidTable句柄表

在 Windows 操作系统内核中&#xff0c;PspCidTable 通常是与进程&#xff08;Process&#xff09;管理相关的数据结构之一。它与进程的标识和管理有关&#xff0c;每个进程都有一个唯一的标识符&#xff0c;称为进程 ID&#xff08;PID&#xff09;。与之相关的是客户端 ID&am…

【蓝桥杯选拔赛真题71】Scratch绘制彩虹 少儿编程scratch图形化编程 蓝桥杯创意编程选拔赛真题解析

目录 scratch绘制彩虹 一、题目要求 编程实现 二、案例分析 1、角色分析

Python+Requests对图片验证码的处理

Requests对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入&#xff0c;而且每次登录时图片验证码都是随机的&#xff1b;当通过request做接口登录的时候要对图片验证码进行识别出图片中的字段&#xff0c;然后再登录接口中使用&#xff1b; 通过request对图片验…

ChatGPT成为“帮凶”:生成虚假数据集支持未知科学假设

ChatGPT 自发布以来&#xff0c;就成为了大家的好帮手&#xff0c;学生党和打工人更是每天都离不开。 然而这次好帮手 ChatGPT 却帮过头了&#xff0c;莫名奇妙的成为了“帮凶”&#xff0c;一位研究人员利用 ChatGPT 创建了虚假的数据集&#xff0c;用来支持未知的科学假设。…

Windows环境 dockertopdesk 部署gitlab

1.在dockertopdesk里搜索 gitlab镜像 (pull)拉取镜像 2.运行镜像到容器 mkdir gitlab gitlab/etc gitlab/log gitlab/opt docker run -id -p 3000:80 -p 9922:22 -v /root/gitlab/etc:/etc/gitlab -v /root/gitlab/log:/var/log/gitlab -v /root/gitlab/opt:/var/opt/gitla…

Linux系统之centos7编译安装Python 3.8

前言 CentOS (Community Enterprise Operating System) 是一种基于 Red Hat Enterprise Linux (RHEL) 进行源代码再编译并免费提供给用户的 Linux 操作系统。 CentOS 7 采用了最新的技术和软件包&#xff0c;并提供了强大的功能和稳定性。它适用于各种服务器和工作站应用场景&a…

8.整数转换为浮点数【2023.11.30】

1.问题描述 整数转换为浮点数。 2.解决思路 使用input函数读取输入的整数 input_int int(input()) #将整数转换为浮点数类型 output_float float(input_int) 3.代码实现 numint(input("请输入一个整数")) num1float(num) print(num1)4.运行结果

掌握HarmonyOS框架的ArkTs如何管理和共享状态数据

ARKTS&#xff08;Ark TypeScript&#xff09;是HarmonyOS应用框架的一部分&#xff0c;提供了一种灵活而强大的状态管理机制。在ARKTS中&#xff0c;AppStorage和LocalStorage是两个关键的概念&#xff0c;它们分别用于应用级和页面级的状态共享。通过深入了解这两个特性&…

【Python】OpenCV库中常用函数详解和示例

在Python中&#xff0c;OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个广泛使用的图像和视频处理库。它包含许多用于图像处理和计算机视觉任务的函数。本文对一些常用的OpenCV函数及其详细解释和示例&#xff0c;以帮助大家理解和使用。 目录 cv2.…

跨模态图像翻译:使用具有感知监督的多生成网络合成MR脑图像的CT图像

Cross-modality image translation: CT image synthesis of MR brain images using multi generative network with perceptual supervision 跨模态图像翻译&#xff1a;使用具有感知监督的多生成网络合成MR脑图像的CT图像背景贡献实验方法损失函数Thinking 跨模态图像翻译&…

使用 NRF24L01 无线收发模块进行远程控制

NRF24L01 是一款基于 2.4GHz 射频通信的低功耗无线收发模块&#xff0c;具有高性能和稳定性&#xff0c;适用于远程控制和数据传输应用。本文将介绍如何使用 NRF24L01 模块进行远程控制&#xff0c;包括硬件的连接和配置&#xff0c;以及相应的代码示例。 一、引言 NRF24L01 是…

es6之class类(未完成)

es6之class类 一、什么是类二、类的基本用法1.定义类2.constructor() 方法3.静态方法&#xff08;属性)4.私有方法&#xff08;属性&#xff09; 三、继承 一、什么是类 类是用于创建对象的模板&#xff0c;类只是让对象原型的写法更加清晰、更像面向对象编程的语法。 class Pe…

matlab 多目标粒子群优化算法MOPSO

1、内容简介 略 21-可以交流、咨询、答疑 多目标、粒子群 2、内容说明 多目标粒子群优化算法MOPSO 3、仿真分析 略 %% Problem Definition TestProblem3; % Set to 1, 2, or 3 switch TestProblem case 1 CostFunction(x) MyCost1(x); nVar5; …

REST-Assured--JAVA REST服务自动化测试的Swiss Army Knife

什么是REST-Assured REST Assured是一套基于 Java 语言实现的开源 REST API 测试框架 Testing and validation of REST services in Java is harder than in dynamic languages such as Ruby and Groovy. REST Assured brings the simplicity of using these languages into t…

TCP简介及特性

1. TCP协议简介 TCP是Transmission Control Protocol的简称&#xff0c;中文名是传输控制协议。它是一种面向连接的、可靠的、基于IP的传输层协议。两个TCP应用之间在传输数据的之前必须建立一个TCP连接&#xff0c;TCP采用数据流的形式在网络中传输数据。TCP为了保证报文传输的…

网站更换IP的四大注意事项

1.对网站当中的数据进行备份 网站更换IP时可以将页面的数据库文件和站点文件通过下载工具在本地完成备份。 2.更换解析域名 从站点域名管理后台当中更换域名地址&#xff0c;改为新的IP地址。 3.确保IP安全 在用户更换IP前一定要确定IP是否安全&#xff0c;一旦IP存在不良…