Spring Boot+FreeMarker=打造高效Web应用

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于Spring Boot+FreeMarker的相关操作吧 

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一. FreeMarker是什么

二.为什么要使用 FreeMarker 

三. 怎么使用

1.创建目录存放FreeMarker模板文件

2.添加相应的pom依赖&yml的相关配置

3.启用 FreeMarker 支持

4.创建 FreeMarker 模板文件

5.初步使用 FreeMarker

四.FreeMarker的基本语法

1.数据类型

①.字符串

②.数值

③.布尔值

④.日期

2.常见指令

1.处理不存在的值

2.assign

3.if/elseif/else

4.list

5.include

五. 使用FreeMarker+Spring Boot高效的完成增删改查

Controller层

ftl文件

 common.ftl

index.ftl

 六.易错

1.@Controller和@RestController的区别

2.文件的嵌套问题


一. FreeMarker是什么

        FreeMarker是一种模板引擎,用于生成动态内容。它是一个基于Java的开源项目,主要用于在Web应用程序中生成动态的HTML、XML、JSON等文档。FreeMarker通过将数据和模板结合,可以轻松地生成动态的输出。

        使用FreeMarker,您可以将模板与数据源(例如Java对象或数据库)结合,以生成最终的输出。模板中可以包含条件语句、循环、变量替换等逻辑,使得生成的内容可以根据不同的数据和条件进行动态调整。

        FreeMarker具有良好的可扩展性和灵活性,广泛应用于Java Web开发领域。它被许多Java框架和工具广泛采用,如Spring MVC、Apache Struts等

二.为什么要使用 FreeMarker 

  1. 分离逻辑和展示层:FreeMarker 提供了一种模板引擎的方式,可以将业务逻辑和页面展示完全分离。这样,开发人员可以专注于编写业务逻辑,而设计师可以专注于美化页面,提高团队协作效率。

  2. 强大的模板功能:FreeMarker 提供了丰富的模板语法和功能,包括条件判断、循环、变量定义等。这使得在模板中处理数据和控制页面展示变得非常灵活和方便。

  3. 支持多种输出格式:FreeMarker 不仅可以生成 HTML 页面,还可以生成其他格式的文本,如 XML、JSON 等。这使得它成为构建多种类型应用的理想选择。

  4. 提高性能:FreeMarker 通过对模板进行预编译和缓存,可以有效地提高渲染速度。此外,它还提供了一些优化选项,如延迟加载、按需引入模板等,进一步提升性能。

  5. 大量的模板库和社区支持:FreeMarker 拥有庞大的模板库和活跃的社区,你可以轻松找到各种现成的模板和解决方案,加快开发速度

三. 怎么使用

1.创建目录存放FreeMarker模板文件

在项目中创建一个目录来存放 FreeMarker 模板文件。可以将它放在源代码目录下的任何位置,例如 src/main/resources/templates

2.添加相应的pom依赖&yml的相关配置

在 Maven的pom.xml 文件中添加以下依赖项

<!--freemarker--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>

application.yml

#freemarker的相关配置freemarker:# 设置模板后缀名suffix: .ftl# 设置文档类型content-type: text/html# 设置页面编码格式charset: UTF-8# 设置页面缓存cache: false# 设置ftl文件路径template-loader-path: classpath:/templates# 设置静态文件路径,js,css等mvc:static-path-pattern: /static/**

3.启用 FreeMarker 支持

按照以下步骤新建好模板文件便可

滑到最上面,找到HTML,复制里面的内容

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>#[[$Title$]]#</title>
</head>
<body>
#[[$END$]]#
</body>
</html>

4.创建 FreeMarker 模板文件

5.初步使用 FreeMarker

package com.wh.springboot.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;/*** @author是辉辉啦* @create 2023-12-13-23:01*/
@Controller
public class TestController {@RequestMapping("/")public String index(){return "index";//这里要与你的ftl的文件名相同}}

index.ftl

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>辉辉</title>
</head>
<body>
是辉辉啦!!
</body>
</html>

四.FreeMarker的基本语法

1.数据类型

①.字符串

方法含义
?substring(start,end)截取字符串(左闭右开)
?uncap_first首字母小写输出
?cap_first首字母大写输出
?lower_case字母转小写输出
?upper_case字母转大写输出
?length获取字符串长度
?starts_with("xx")?string是否以指定字符开头(boolean类型)
?ends_with("xx")?string是否以指定字符结尾(boolean类型)
?index_of("xx")获取指定字符的索引
?trim去除字符串前后空格
?replace("xx","xx")替换指定字符串

字符串空值情况处理:

FreeMarker 的变量必须赋值,否则就会抛出异常。而对于 FreeMarker 来说,null 值和不存在的变量是完全一样的,因为 FreeMarker 无法理解 null 值。

②.数值

输入不带引号的数字就可以直接指定一个数字, 必须使用点作为小数的分隔符而不能是其他的分组分隔符。

${0.45}<br>
${18}<br>
<#-- 将数值转换成字符串输出 -->
${1000?c} <br>
<#-- 将数值转换成货币类型的字符串输出 -->
${1000?string.currency} <br>
<#-- 将数值转换成百分比类型的字符串输出 -->
${0.45?string.percent} <br>
<#-- 将浮点型数值保留指定小数位输出 (##表示保留两位小数) -->
${0.45723123?string["0.##"]} <br>

③.布尔值

直接写 true 或者 false 就表示一个布尔值了,不需使用引号。

在freemarker中布尔类型不能直接输出;如果输出要先转成字符串

${flag?c}<br>
${flag?string}<br>
${flag?string("yes","no")}<br>

④.日期

日期变量可以存储和日期/时间相关的数据。

在freemarker中日期类型不能直接输出;如果输出要先转成日期型或字符串

输出方式说明
?date年月日
?time时分秒
?datetime年月日时分秒
?string("自定义格式")指定格式
<#-- 输出日期格式 -->
${createDate?date} <br>
<#-- 输出时间格式 -->
${createDate?time} <br>
<#-- 输出日期时间格式 -->
${createDate?datetime} <br>
<#-- 输出格式化日期格式 -->
${createDate?string("yyyy年MM月dd日 HH时mm分ss秒")} <br>

2.常见指令

1.处理不存在的值

当试图访问一个不存在的变量时, FreeMarker 将会报错而导致模板执行中断。 通常我们可以使用两个特殊操作符来压制这个错误,控制这种错误情况。被控制的变量可以是顶层变量,哈希表或序列的子变量。 此外这些操作符还能处理方法调用的返回值不存在的情况。

  • 默认值操作符

使用形式:

unsafe_expr!default_expr 或 
unsafe_expr! or (unsafe_expr)!default_expr 或 
(unsafe_expr)!

这个操作符允许你为可能不存在的变量指定一个默认值。

${message!"default Value."}
<#assign message="wh">
${message!"default Value."}

输出结果如下:

default Value.
wh

如果默认值被省略了,那么结果将会是空串,空序列或空哈希表。(这是 FreeMarker 允许多类型值的体现)请注意,如果想让默认值为 0false,则不能省略它。

(${message!})
<#assign message = "wh">
(${message!})
  • 不存在值检测操作符

使用形式:

 unsafe_expr?? 或(unsafe_expr)??

这个操作符告诉我们一个值是否存在。基于这种情况, 结果是 truefalse

<#if name??>存在
<#else>不存在
</#if>
  • exists用在逻辑判断

exists用作逻辑判断,返回的是true或者false。

<#if name?exists>${name}
</#if>
  • if_exists用来打印东西

if_exists用于输出的时候,如果存在则输出,不存在就输出空字符串。

${name?if_exists}

2.assign

使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量。语法格式如下:

<#assign name1=value1 name2=value2 ... nameN=valueN>
或
<#assign name>capture this
</#assign>

案例演示:

<#-- 创建一个str的变量 -->
<#assign str="hello">
<#-- 输出str -->
${str} <br>
<#-- 一次创建多个变量 -->
<#assign num=1 names=["zhangsan","lisi","wangwu"] >
${num} -- ${names?join(",")}

3.if/elseif/else

你可以使用 ifelseifelse 指令来条件判断是否越过模板的一个部分。 *condition* 必须计算成布尔值, 否则错误将会中止模板处理。elseifelse 必须出现在 if 内部 (也就是,在 if 的开始标签和结束标签之间)。 if 中可以包含任意数量的 elseif(包括0个) 而且结束时 else 是可选的。

<#if condition>...
<#elseif condition2>...
<#elseif condition3>...
...
<#else>...
</#if>

4.list

list 指令执行在 list 开始标签和 list 结束标签 ( list 中间的部分) 之间的代码, 对于在序列(或集合)中每个值指定为它的第一个参数。 对于每次迭代,循环变量将会存储当前项的值。

循环变量仅仅存在于 list 标签体内。 而且从循环中调用的宏/函数不会看到它(就像它只是局部变量一样)。

<#list sequence as item>Part repeated for each item
<#else>Part executed when there are 0 items
</#list>

注意:

  • else 部分是可选的, 而且仅仅从 FreeMarker 2.3.23 版本开始支持。

  • sequence: 将我们想要迭代的项,算作是序列或集合的表达式

  • item: 循环变量的名称 (不是表达式)

<#list arrs as item>${item}
<#else>集合是空的
</#list>

5.include

可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path 参数指定)

<#include path>
或
<#include path options>

这里:

  • path: 要包含文件的路径;

  • options: 一个或多个这样的选项: encoding=encoding, parse=parse

    • encoding: 算作是字符串的表达式

    • parse: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值)

    • ignore_missing: 算作是布尔值的表达式

<h1>Hello Freemarker</h1>
<#include "/common/head.ftl">

五. 使用FreeMarker+Spring Boot高效的完成增删改查

Controller层

package com.wh.springboot.controller;
import com.github.pagehelper.PageHelper;
import com.sun.org.apache.regexp.internal.RE;
import com.wh.springboot.mapper.TBookMapper;
import com.wh.springboot.model.TBook;
import com.wh.springboot.page.PageAnnotation;
import com.wh.springboot.page.PageBean;
import com.wh.springboot.service.IBookService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/*** @author是辉辉啦* @create 2023-12-12-14:44*/
@Controller
@RequestMapping("/book")
public class BookController {@Autowired
private IBookService bookService;//        @RequestMapping("/list")
//        public Object hello(PageBean pageBean){
//            PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
//            return bookService.select(pageBean);
//    }@RequestMapping("/list")public String  list(Model model){List<TBook> select = bookService.select();model.addAttribute("book",select);return "index";}@RequestMapping("/add")public String  add(TBook book){bookService.insertSelective(book);return "redirect:/book/list" ;}@RequestMapping("/edit")public String  edit(TBook book){bookService.updateByPrimaryKeySelective(book);return "redirect:/book/list" ;}@RequestMapping("/del")public String del(TBook book){bookService.deleteByPrimaryKey(book);return "redirect:/book/list" ;}}

ftl文件

 common.ftl

<#--项目根目录-->
<#assign wh="${springMacroRequestContext.contextPath}">
<#--css-->
<link rel="stylesheet" href="${wh}/bootstrap-3.4.1-dist/css/bootstrap.css">
<#--jquery-->
<script src="${wh}/jquery-3.6.1.js"></script>
<#--js-->
<script src="${wh}/bootstrap-3.4.1-dist/js/bootstrap.js"></script>

index.ftl

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>辉辉</title><#include "common.ftl">
</head>
<body>
<h1 align="center">🥰🥰是辉辉啦🥰🥰</h1>
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#myModal">新增
</button><#if book??><table class="table table-hover"><#list book as b><tr><td>${b.id}</td><td>${b.bookname}</td><td>${b.price}</td><td>${b.booktype}</td><td><a href="${wh}/book/del?id=${b.id}">删除</a></td><td><button type="button" class="btn btn-warning" data-toggle="modal"onclick="editBooks(${b.id},'${b.bookname}',${b.price},'${b.booktype}')">修改</button></td></tr></#list></table>
</#if><!-- 触发模态框的按钮 --><!-- 模态框 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="myModalLabel">新增图书</h5><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"><!-- 表单 --><form id="bookForm"><div class="form-group"><label for="bookname">名称</label><input type="text" class="form-control" id="bookname"></div><div class="form-group"><label for="price">价格</label><input type="text" class="form-control" id="price"></div><div class="form-group"><label for="booktype">类型</label><input type="text" class="form-control" id="booktype"></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button><button type="button" class="btn btn-primary" onclick="saveBook()">确认新增</button></div></div></div>
</div><!-- 模态框 -->
<div class="modal fade" id="update" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="myModalLabel">修改图书</h5><button type="button" class="close" data-dismiss="update" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"><!-- 表单 --><form><label for="id">ID:</label><input type="text" id="ids" readonly><br><label for="bookname">Name:</label><input type="text" id="booknames" ><br><label for="price">Price:</label><input type="text" id="prices"><br><label for="booktype">Type:</label><input type="text" id="booktypes"><br></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button><button type="button" class="btn btn-primary" onclick="editBook()">确认修改</button></div></div></div>
</div><script>function editBooks(id,bookname,price,booktype) {// console.log(id,bookname,price,booktype);document.getElementById("ids").value = id;document.getElementById("booknames").value = bookname;document.getElementById("prices").value = price;document.getElementById("booktypes").value = booktype;// // 关闭模态框$('#update').modal('show');}function editBook() {// 获取表单中的数据var id = document.getElementById("ids").value;var bookname = document.getElementById('booknames').value;var price = document.getElementById('prices').value;var booktype = document.getElementById('booktypes').value;$.ajax({url: '${wh}/book/edit', // 请求的 URLtype: 'POST', // 请求类型,可以是 GET、POST 等data: {id:id,bookname: bookname, price: price, booktype: booktype  }, // 发送到服务器的数据success: function(response) {// 当请求成功时的处理逻辑alert('修改成功');},error: function(xhr, status, error) {// 当请求失败时的处理逻辑alert('修改失败');}});// 关闭模态框$('#update').modal('hide');}function saveBook() {// 获取表单中的数据var bookname = document.getElementById('bookname').value;var price = document.getElementById('price').value;var booktype = document.getElementById('booktype').value;console.log(bookname+price+booktype)$.ajax({url: '${wh}/book/add', // 请求的 URLtype: 'POST', // 请求类型,可以是 GET、POST 等data: {bookname: bookname, price: price, booktype: booktype  }, // 发送到服务器的数据success: function(response) {// 当请求成功时的处理逻辑alert('增加成功');},error: function(xhr, status, error) {// 当请求失败时的处理逻辑alert('增加失败');}});// 关闭模态框$('#myModal').modal('hide');}
</script>
</body>
</html>

 六.易错

1.@Controller和@RestController的区别

2.文件的嵌套问题

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

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

相关文章

本地连锁门店经营可以借助系统实现哪些功能?

不少的连锁门店目前还是很基础的ERPPOS收银&#xff0c;其他的还是走传统的手工管理&#xff0c;大多连锁老板知道借助信息化系统可以帮助门店实现精细化管理&#xff0c;提高运营效率&#xff0c;降低成本&#xff0c;增强竞争力&#xff0c;但不知道怎么去做&#xff0c;能做…

每日汇评:黄金需要突破2050美元的供应区域才能延续复苏

周四早间&#xff0c;金价接近每盎司2,030美元&#xff0c;创下6天来的最高水平&#xff1b; 美联储确认鸽派政策转向&#xff0c;美元和美国国债收益率双双下挫&#xff1b; 英国央行和欧洲央行2023年的最终政策公告可能会进一步推高金价&#xff1b; 随着投资者重新评估美联储…

2020年第九届数学建模国际赛小美赛C题亚马逊野火解题全过程文档及程序

2020年第九届数学建模国际赛小美赛 C题 亚马逊野火 原题再现&#xff1a; 野火是指发生在乡村或荒野地区的可燃植被中的任何不受控制的火灾。这样的环境过程对人类生活有着重大的影响。因此&#xff0c;对这一现象进行建模&#xff0c;特别是对其空间发生和扩展进行建模&…

0x13 链表与邻接表

0x13 链表与邻接表 数组是一种支持随机访问&#xff0c;但不支持在任意位置插入和删除元素的数据结构。与之相对应&#xff0c;链表支持在任意位置插入或删除元素&#xff0c;但只能按顺序依次访问其中元素。我们可以使用一个struct来表示链表的节点&#xff0c;其中可以存储任…

《师兄啊师兄》第二季开播 李长寿渡劫归来扬名四海

看新国风&#xff0c;上优酷动漫&#xff01;由优酷出品&#xff0c;玄机科技制作&#xff0c;改编自阅文集团旗下起点读书小说《我师兄实在太稳健了》&#xff08;作者&#xff1a;言归正传&#xff09;的修仙喜剧动画《师兄啊师兄》第二季《海神扬名篇》于今日10:00正式回归。…

如何性能测试中进行业务验证?

在性能测试过程中&#xff0c;验证HTTP code和响应业务code码是比较基础的&#xff0c;但是在一些业务中&#xff0c;这些参数并不能保证接口正常响应了&#xff0c;很可能返回了错误信息&#xff0c;所以这个时候对接口进行业务验证就尤其重要。下面分享一个对某个资源进行业务…

Python多线程threading的使用方法

前言 有时候&#xff0c;我们在编写Python程序时&#xff0c;会遇到比较耗时的函数方法&#xff0c;我们的需求是等这个耗时的函数执行完毕之后&#xff0c;在执行后面的程序&#xff0c;这时候就需要用到多进程。 下面我们来举一个使用多进程threading的例子 例子 import t…

Unity | AVpro的最基础使用方法(视频播放插件)

一、 AVpro的使用方法 (一)准备播放器MediaPlayer 1. AVpro的播放器是MediaPlayer&#xff0c;在Heirarchy面板里创建 2.播放器里放视频 a.把视频放到StreamingAssets文件夹下 b.你就可以在MediaPlayer里面找到这个视频 c.选中以后&#xff0c;就会变成 这里点击播放可以播放…

FET偏置控制器电路的卫星接收器LNB电路

都具有FET偏置控制器电路的卫星接收器LNB电路 芯片的描述&#xff1a;D3211是一-块用于卫星接收LNBs的专用电路&#xff0c;具有极化电压检测切换、22KHz脉冲检测切换和提供高放、本振级GaAs或HEMT FET晶体管工作点偏置等功能。D321 1内部的22K检测及切换控制由22K有源滤波器、…

RT-DETR改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定义模型

💡该教程为改进RT-DETR专栏,属于《芒果书》📚系列,包含大量的原创改进方式🚀 💡🚀🚀🚀内含改进源代码 按步骤操作运行改进后的代码即可💡更方便的统计更多实验数据,方便写作 RT-DETR改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练…

车联网助力自动驾驶发展

单车智能决策难点 芯片&#xff0c;成为自动驾驶的最大瓶颈 自动驾驶对芯片算力要求极高。要求自动驾驶处理器在每秒能够处理数百万亿次的计算&#xff1b; 自动驾驶对计算的实时性要求极高。任何一点时延&#xff0c;都有可能造成车毁人亡&#xff1b; 对低能耗有极大的…

Java并发编程基础总结

进程和线程概念 什么进程 进程是系统运行的基本单位&#xff0c;通俗的理解我们计算机启动的每一个应用程序都是一个进程。如下图所示&#xff0c;在Windows中这一个个exe文件&#xff0c;都是一个进程。而在JVM下&#xff0c;每一个启动的Main方法都可以看作一个进程。 什么…

如何用gpt改写文章 (1) 神码ai

大家好&#xff0c;今天来聊聊如何用gpt改写文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何用GPT改写文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然语言处理领域取得了重大突…

【数电笔记】54-或非门构成的基本RS触发器

目录 说明&#xff1a; 1. 电路组成 2. 逻辑功能 3. 特性表 4. 特性方程 5. 例题 6. 两种基本RS触发器的形式比 说明&#xff1a; 笔记配套视频来源&#xff1a;B站&#xff1b;本系列笔记并未记录所有章节&#xff0c;只对个人认为重要章节做了笔记&#xff1b;标题前…

02-详解请求路由的实现和常见的断言工厂

请求路由 路由转发 第一步: 新建一个SpringBoot工程如gateway模块, 引入网关依赖和nacos服务发现依赖 <!--网关依赖--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId&…

玄关柜和鞋柜是一回事吗?福州中宅装饰,福州装修

玄关柜和鞋柜虽然都用于存放鞋子&#xff0c;但它们在概念上有所不同。玄关柜是一个更大的概念&#xff0c;它包括鞋柜和其他功能区域&#xff0c;可以说鞋柜是玄关柜的一部分。 1️⃣概念上的不同 玄关柜是一种集成了鞋柜、挂衣架、换鞋凳等多种功能于一体的家居家具&#xf…

Nginx(四层+七层代理)+Tomcat实现负载均衡、动静分离

一、Tomcat多实例部署 具体步骤请看我之前的博客 写文章-CSDN创作中心https://mp.csdn.net/mp_blog/creation/editor/134956765?spm1001.2014.3001.9457 1.1 访问测试多实例的部署 1.2 分别在三个tomcat服务上部署jsp的动态页面 mkdir /usr/local/tomcat/webapps/test vim …

Fork和Join底层原理

文章目录 一、任务类型1. 简介2. CPU密集型3. IO密集型4. 线程数计算方法 二、Fork/Join框架1. 思想2. Fork/Join简介3. Fork/Join使用4. 底层原理5. 总结 一、任务类型 1. 简介 思考: 线程池的线程数设置多少合适? 我们调整线程池中的线程数量的最主要的目的是为了充分并合理…

数字孪生轻量化引擎——AMRT3D引擎

随着全球经济亟待复苏&#xff0c;作为科技发展主要需求技术之一&#xff0c;数字孪生已经成为全球多个国家重点布局行业。例如&#xff0c;美国工业互联网盟将数字孪生作为工业互联网落地的核心和关键&#xff0c;德国工业4.0参考架构将数字孪生作为重要内容。 数字孪生已经形…

SSRF攻击实例讲解

服务器端请求伪造&#xff08;SSRF&#xff09;攻击是一种网络安全漏洞&#xff0c;其中攻击者迫使受影响的服务器向攻击者指定的内部或外部系统发送请求。以下是一个SSRF攻击的实例讲解及其分析。 SSRF攻击实例 当然&#xff0c;下面提供另外三个SSRF&#xff08;服务器端请…