【SpringBoot笔记18】SpringBoot实现统一异常处理、统一结果响应、统一参数校验

news2024/11/13 15:11:05

这篇文章,主要介绍如何利用SpringBoot框架实现统一异常处理、统一结果响应、统一参数校验。

目录

一、SpringBoot统一结果响应

1.1、创建工程

1.2、配置FastJson

1.3、创建ResultEnum枚举

1.4、创建Result实体类

二、SpringBoot统一异常处理

2.1、创建自定义异常类

2.2、创建全局异常处理类

三、SpringBoot统一参数校验

3.1、引入参数校验依赖

3.2、创建测试实体类

3.3、创建测试控制器

3.4、运行测试


在实际的项目开发过程中,当应用程序发生异常时,用户可以接收到友好的错误提示信息,而不是直接出现一大堆看不懂的错误信息,这就需要对应用程序的所有异常进行统一的处理,SpringBoot框架提供了统一异常处理的注解,通过相应的注解就可以捕获所有可能出现的异常信息。

此外,在前后端分离的模式下,数据的交互都是采用JSON格式来传递的,如果每一个方法都返回不同格式的数据,这显然不太合适,所以,实际开发中都会将响应结果统一处理,并且对前后端传递的参数进行统一的校验。下面具体介绍如何实现三个统一。

一、SpringBoot统一结果响应

统一结果响应,这里是采用的JSON格式响应所有的数据,所以需要使用FastJson依赖。

1.1、创建工程

首先,创建一个SpringBoot工程,工程中需要引入下面几个依赖。

<!-- 引入 SpringBoot 父工程依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.0.RELEASE</version>
</parent>

<dependencies>
    <!-- 引入 web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!-- 排除 jackson 依赖 -->
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-json</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- 引入 fastjson 依赖 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.77</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

1.2、配置FastJson

在工程里面,创建一个【CustomFastJsonConfig】配置类,添加如下配置。

package com.spring.boot.demo.common.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:07
 * @Description FastJson 配置类
 */
@Configuration
public class CustomFastJsonConfig {

    @Bean
    public HttpMessageConverters fastjsonHttpMessageConverters() {
        // 创建 FastJsonHttpMessageConverter 消息转换器对象
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();

        // 创建 FastJsonConfig 配置类对象
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        // 设置编码字符集
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        // 设置日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

        // 设置序列化特征: SerializerFeature 是一个枚举,可以选择不同的序列化特征
        SerializerFeature[] serializerFeatures = new SerializerFeature[]{
                // WriteNullStringAsEmpty: 如果字符串等于 null,那么会被序列化成空字符串 ""
                SerializerFeature.WriteNullStringAsEmpty,
                // WriteNullNumberAsZero: 如果数字等于 null,那么会被序列化成 0
                SerializerFeature.WriteNullNumberAsZero,
                // WriteNullBooleanAsFalse: 如果布尔类型等于 null,那么会被序列化成 false
                SerializerFeature.WriteNullBooleanAsFalse,
                // PrettyFormat: 美化JSON
                SerializerFeature.PrettyFormat
        };
        fastJsonConfig.setSerializerFeatures(serializerFeatures);

        // 配置添加到消息转换器里面
        fastJsonHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

        // 设置响应JSON格式数据
        List<MediaType> mediaTypeList = new ArrayList<>();
        mediaTypeList.add(MediaType.APPLICATION_JSON); // JSON 格式数据
        // 设置消息转换器支持的格式
        fastJsonHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);

        // 返回消息转换器
        return new HttpMessageConverters(fastJsonHttpMessageConverter);
    }
}

1.3、创建ResultEnum枚举

在一个工程里面,可能会出现很多不同的响应结果状态,所以这里采用一个【ResultEnum】枚举类来保存。

  • 这里可以根据自己的需要,继续添加不同的枚举类型。
package com.spring.boot.demo.common.resp;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:13
 * @Description 统一响应结果枚举类
 */
public enum ResultEnum {
    SUCCESS(20000, "响应成功"),
    FAILED(50000, "操作异常");
    
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;

    ResultEnum(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

1.4、创建Result实体类

为了实现统一响应结果,这里创建一个【Result】类,该类作为所有控制器方法的返回值类型。

package com.spring.boot.demo.common.resp;

import java.io.Serializable;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:16
 * @Description 统一响应结果类
 */
public class Result<T> implements Serializable {
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;
    /** 响应数据 */
    private T data;

    /** 私有构造方法 */
    private Result() {}

    /************************** common method **************************/
    /* 响应成功 */
    public static <T> Result<T> success() {
        Result<T> result = new Result<>();
        result.setStatusCode(ResultEnum.SUCCESS.getStatusCode());
        result.setMessage(ResultEnum.SUCCESS.getMessage());
        return result;
    }

    /* 响应失败 */
    public static <Void> Result<Void> failure() {
        Result<Void> result = new Result<>();
        result.setStatusCode(ResultEnum.FAILED.getStatusCode());
        result.setMessage(ResultEnum.FAILED.getMessage());
        return result;
    }

    /* 响应失败 */
    public static <Void> Result<Void> failure(String message) {
        Result<Void> result = new Result<>();
        result.setStatusCode(ResultEnum.FAILED.getStatusCode());
        result.setMessage(message);
        return result;
    }

    /* 响应失败 */
    public static <Void> Result<Void> failure(int statusCode, String message) {
        Result<Void> result = new Result<>();
        result.setStatusCode(statusCode);
        result.setMessage(message);
        return result;
    }

    /* 响应失败 */
    public static <Void> Result<Void> failure(ResultEnum resultEnum) {
        Result<Void> result = new Result<>();
        result.setStatusCode(resultEnum.getStatusCode());
        result.setMessage(resultEnum.getMessage());
        return result;
    }

    /* 自定义响应状态码 */
    public Result<T> statusCode(int statusCode) {
        this.setStatusCode(statusCode);
        return this;
    }

    /* 自定义响应提示信息 */
    public Result<T> message(String message) {
        this.setMessage(message);
        return this;
    }

    /* 自定义响应数据 */
    public Result<T> data(T data) {
        this.setData(data);
        return this;
    }

    /************************** getter and setter **************************/
    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Result{" +
                "statusCode=" + statusCode +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

到这里,我们就已经把统一响应结果的类定义好啦,下面继续定义统一全局异常处理的类。

二、SpringBoot统一异常处理

2.1、创建自定义异常类

在工程里面,创建一个自定义的异常类【BizException】,这个类主要用于我们在编写业务代码的时候,自定义设置错误的状态码和提示信息。

package com.spring.boot.demo.common.exception;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:30
 * @Description 自定义异常类
 */
public class BizException extends RuntimeException {
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;

    public BizException(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    public BizException() {
        super();
    }

    public BizException(String message) {
        this.message = message;
    }

    public BizException(String message, Throwable cause) {
        super(message, cause);
    }

    public BizException(Throwable cause) {
        super(cause);
    }

    protected BizException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

2.2、创建全局异常处理类

在工程里面,创建一个【GlobalExceptionHandler】类,这个类就是处理整个项目里面出现的所有异常的。

  • 这个类上面,需要使用【@ControllerAdvice】注解,表示该类会通过AOP的方式拦截所有Controller层抛出的所有异常。
  • 在该类里面,定义处理异常的方法,并且使用【@ExceptionHandler】注解,指定当前方法处理的异常类型。
package com.spring.boot.demo.common.exception;

import com.spring.boot.demo.common.resp.Result;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:00
 * @Description 全局异常处理类
 */
@ControllerAdvice // 指定AOP拦截Controller层方法
public class GlobalExceptionHandler {

    /**
     * 统一异常处理
     */
    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public Result<Void> commonHandler(Exception e) {
        e.printStackTrace();
        // 返回错误信息
        return Result.failure(e.getMessage());
    }

    /**
     * 捕获我们自定义的异常
     */
    @ExceptionHandler(value = {BizException.class})
    @ResponseBody
    public Result<Void> bizExceptionHandler(BizException e) {
        e.printStackTrace();
        // 返回错误信息
        return Result.failure(e.getMessage());
    }

    /** 参数校验异常处理 */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result<Void> exception(MethodArgumentNotValidException e) {
        e.printStackTrace();
        // 保存错误提示信息
        String message = "参数校验失败";
        // 从异常对象中拿到 ObjectError 对象
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        if (!CollectionUtils.isEmpty(allErrors)) {
            // 返回第一个校验失败的参数名称
            message = allErrors.get(0).getDefaultMessage();
        }
        // 返回参数校验信息
        return Result.failure(message);
    }

}

到这里,全局异常处理类就创建好啦,下面继续创建统一参数校验(注意:上面处理参数校验异常的时候,会报错,那是因为没有引入下面两个参数校验的依赖)。

三、SpringBoot统一参数校验

3.1、引入参数校验依赖

参数校验需要引入validation依赖,如下所示:

<!-- validation参数校验 -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>
<!-- validation参数校验 -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

3.2、创建测试实体类

package com.spring.boot.demo.pojo;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:45
 * @Description
 */
public class User implements Serializable {

    @NotBlank(message = "用户名称不能为空")
    @Size(min = 1, max = 50, message = "用户名称长度必须在1到50之间")
    private String username;

    @NotBlank(message = "用户密码不能为空")
    @Size(min = 1, max = 30, message = "用户密码长度必须在1到30之间")
    private String password;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

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

3.3、创建测试控制器

package com.spring.boot.demo.controller;

import com.spring.boot.demo.common.exception.BizException;
import com.spring.boot.demo.common.resp.Result;
import com.spring.boot.demo.pojo.User;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:44
 * @Description
 */
@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/success")
    public Result<List<User>> success() {
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            User user = new User();
            user.setUsername("text-name00" + i);
            user.setPassword("pass-00" + i);
            user.setEmail("test@qq.com");
            list.add(user);
        }
        return Result.<List<User>>success().data(list);
    }

    @GetMapping("/error")
    public Result<Void> failure() {
        int i = 10;
        // 模拟异常情况
        int ans = i / 0;
        return Result.success();
    }

    @GetMapping("/error02")
    public Result<Void> failure02() {
        int i = 10;
        // 模拟异常情况
        try {
            int ans = i / 0;
        } catch (Exception e) {
            throw new BizException("抛出自定义异常");
        }
        return Result.success();
    }

    @PostMapping("/error03")
    public Result<User> param(@RequestBody @Valid User user) {
        // 测试参数校验异常, 必须使用【@Valid】注解生效
        return Result.<User>success().data(user);
    }

}

3.4、运行测试

启动工程,使用Postman一次访问上面四个请求地址,可以看到返回结果。

到此,SpringBoot统一异常处理、统一结果响应、统一参数校验就成功啦。

综上,这篇文章结束了,主要介绍如何利用SpringBoot框架实现统一异常处理、统一结果响应、统一参数校验。

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

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

相关文章

【LeetCode 784. 字母大小写全排列】异或进行字母大小写变换

LeetCode784.字母大小写全排列解题方法&#xff1a;回溯用异或进行大小写变换正式解题总代码784.字母大小写全排列 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺…

htm+JS实现绘制数学函数绘图

htmJS实现绘制数学函数绘图 其中常用的函数可参见&#xff0c;JS中常用的Math方法 https://blog.51cto.com/u_10725738/5253407 https://www.jb51.net/article/9151.htm JS(JavaScript)常用Math函数与用法说明 方法描述示例abs(x)返回数的绝对值。Math.abs(-10)//返回10aco…

【Linux】--谈谈冯诺依曼体系结构和操作系统

文章目录冯诺依曼体系组成部分数据层面CPU存储器外设结论操作系统什么是操作系统为什么要有操作系统怎么去管理总结冯诺依曼体系 冯诺依曼结构是一种将程序指令存储器和数据存储器合并在一起的存储器结构&#xff0c;数学家冯诺依曼提出了计算机制造的三个基本原则&#xff0c…

C++继承、多继承及菱形继承

继承 继承是C面向对象的三大特性之一&#xff08;封装、继承和多态&#xff09;。 一、构造和析构顺序 先执行父类构造函数&#xff0c;再执行子类构造函数&#xff1b; 先执行子类析构函数&#xff0c;在执行父类析构函数。 二、同名隐藏 2.1 概念 子类中的同名属性和成…

vue父子组件传值记录

之前父子组件传值不太熟悉&#xff0c;组件间传值要么用vuex&#xff0c;要么用sessionStorage缓存&#xff0c;但是sessionStorage感觉用多了不太好&#xff0c;缓存太多容易混淆&#xff0c;vuex还是比较好用&#xff0c;&#xff0c;但是用的过程中好像有个问题&#xff0c;…

Shell之计算命令、流程控制、函数

文章目录&#x1f68f; Shell计算命令&#x1f680; Shell计算命令&#xff1a;expr命令详解&#x1f6ac; 求值表达式(整数 前面)&#x1f6ac; 字符串语法&#x1f6ac; 小结&#x1f684; Shell计算命令&#xff1a;(())命令详解&#x1f6ac; 小结&#x1f692; Shell计算命…

linux:2.3.4 查找/搜索命令(find+grep)+压缩/解压缩命令(gzipbzip2+tar+mv+mkdir)

2.3.4 查找/搜索命令 1. find 在 Windows 中搜索文件&#xff0c;一般查找文件需要传入两个条件&#xff1a;① 在哪些目录中查找&#xff1b; ② 查找的内容.。在 Linux 中&#xff0c;查找文件的也需要这两个条件&#xff0c;不同于 Windows 使用搜索框 查找&#xff0c;Li…

蓝桥杯单片机第九届省赛题详细讲解(电子钟)

看之前强烈建议先自己做一遍&#xff01;&#xff01;&#xff01;演示视频题目讲解完整程序main.conewire.honewire.cds1302.hds1302.c工程文件演示视频 题目讲解 首先还是一如既往从题目的程序框图准备起。 将ds18b20&#xff0c;独立按键&#xff0c;数码管显示&#xff0…

弹指间计算机协会 2021“千里码”程序设计竞赛 题面

A.Hello World! Description 为保证所有参赛人员在比赛中都能拿到分数&#xff0c;本题只考察c的输出。 算法比赛不仅考察思考问题的能力&#xff0c;也需要一定的细心程度。 直接输出" HelI0 Wor1d!&#xff01;" &#xff0c;您将获得此题的全部分数。 Input 本…

django基于python的旅游网站--python-计算机毕业设计

项目介绍 旅游网站系统的用户分管理员和用户两个角色的权限子模块。 管理员所能使用的功能主要有&#xff1a;首页、个人中心、用户管理、景点分类管理、景点信息管理、门票预订管理、酒店信息管理、客房信息管理、客房预订管理、美食信息管理、用户分享、系统管理等。 用户可…

[MySQL]变量

文章目录1. 变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量查看所有系统变量查询部分系统变量查看指定系统变量1.1.3 修改系统变量方式一&#xff1a;修改配置文件方式二&#xff1a;使用 set 设置系统变量的值1.2 用户变量1.2.1 用户变量的分类1.2.2 会话用户变量变量…

Maven

Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构提供了一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布...&#xff09;提供了一套依赖管理机制标准化的项目结构 不同IDE之…

C++语法——详细剖析多态与虚函数

目录 一.虚函数与多态的概念与基本使用 &#xff08;一&#xff09;.概念 &#xff08;二&#xff09;.基本使用 二.虚函数的底层 三.特殊的虚函数&#xff08;协变&#xff09; 四.多态在多继承、菱形继承与菱形虚拟继承中的使用。 &#xff08;一&#xff09;.多继承 …

C语言分支与循环实战篇-猜数字小游戏/关机小程序

前言 &#x1f47b;作者&#xff1a;龟龟不断向前 &#x1f47b;简介&#xff1a;宁愿做一只不停跑的慢乌龟&#xff0c;也不想当一只三分钟热度的兔子。 &#x1f47b;专栏&#xff1a;C初阶知识点 &#x1f47b;工具分享&#xff1a; 刷题&#xff1a; 牛客网 leetcode笔记软…

大一学生HTML5期末大作业——基于HTML+CSS制作传统节日美食13页(美食网站设计与实现)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

[LeetCode周赛复盘] 第 317 场周赛20221030

[LeetCode周赛复盘] 第 317 场周赛20221030 一、本周周赛总结二、 [Easy] 6220. 可被三整除的偶数的平均值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6221. 最流行的视频创作者1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6222. 美丽整数的最小增量1. 题目描述2. 思路…

终极Hadoop大数据教程

终极Hadoop大数据教程 包含 MapReduce、HDFS、Spark、Flink、Hive、HBase、MongoDB、Cassandra、Kafka 等的数据工程和 Hadoop 教程&#xff01; 课程英文名&#xff1a;The Ultimate Hands-On Hadoop - Tame your Big Data! 此视频教程共17.0小时&#xff0c;中英双语字幕&…

1.3 信号处理函数,创建worker进程

文章目录1、信号处理函数2、创建worker线程3、sigsuspend函数说明4、write函数思考1、信号处理函数 1、初始化信号的函数&#xff0c;用于注册信号处理程序 2、信号处理函数 初始化信号函数&#xff0c;遍历结构体数组&#xff0c;然后给结构体数组中的每个成员注册信号处理函数…

python实现自动检测核酸用码记录 ---- 自动化办公小技巧(摸鱼利器)

自动检测核酸用码记录&#x1f947;预备知识✈️os库os.path.exists()os.mkdir()os.remove()os.listdir()&#x1f47d;Python 3 查看字符编码方法⏰python3获取当前系统时间&#x1f424;读取图片&#xff0c;保存到指定目录&#x1f47c;将数据保存到csv文件中&#x1f948;p…

使用html+css实现一个静态页面(含源码)

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…