系列十一、拦截器(二)#案例演示

news2024/12/25 12:26:18

一、案例演示

说明:如下案例通过springboot的方式演示拦截器是如何使用的,以获取Controller中的请求参数为切入点进行演示

1.1、前置准备工作

1.1.1、pom

<dependencies>
	<!-- spring-boot -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>

	<!-- 工具 -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.76</version>
	</dependency>
	<dependency>
		<groupId>commons-beanutils</groupId>
		<artifactId>commons-beanutils</artifactId>
		<version>1.9.3</version>
	</dependency>
	<dependency>
		<groupId>org.apache.httpcomponents</groupId>
		<artifactId>httpclient</artifactId>
		<version>4.5.6</version>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
		<version>3.4</version>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-collections4</artifactId>
		<version>4.3</version>
	</dependency>

	<!-- servlet -->
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>javax.servlet-api</artifactId>
		<version>3.1.0</version>
		<scope>provided</scope>
	</dependency>

</dependencies>

1.1.2、WebRequestConstant

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/5 11:39
 * @Description:
 */
public class WebRequestConstant {

    /**
     * 请求方法:POST
     */
    public static final String METHOD_TYPE_POST = "POST";

    /**
     * 数据类型:application/json
     */
    public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json";

    /**
     * 字符解码
     */
    public static final String CHARACTER_U = "%u";

}

1.1.3、EncodeUtil 

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/5 11:12
 * @Description:
 */
public class EncodeUtil {
    private static final String DEFAULT_URL_ENCODING = "UTF-8";
    private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

    /**
     * Hex编码.
     */
    public static String encodeHex(byte[] input) {
        return Hex.encodeHexString(input);
    }

    /**
     * Hex解码.
     */
    public static byte[] decodeHex(String input) {
        try {
            return Hex.decodeHex(input.toCharArray());
        } catch (DecoderException e) {
            return null;
        }
    }

    /**
     * Base64编码.
     */
    public static String encodeBase64(byte[] input) {
        return Base64.encodeBase64String(input);
    }

    /**
     * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
     */
    public static String encodeUrlSafeBase64(byte[] input) {
        return Base64.encodeBase64URLSafeString(input);
    }

    /**
     * Base64解码.
     */
    public static byte[] decodeBase64(String input) {
        return Base64.decodeBase64(input);
    }

    /**
     * Base62编码。
     */
    public static String encodeBase62(byte[] input) {
        char[] chars = new char[input.length];
        for (int i = 0; i < input.length; i++) {
            chars[i] = BASE62[(input[i] & 0xFF) % BASE62.length];
        }
        return new String(chars);
    }
    /**
     * Html 转码.
     */
    public static String escapeHtml(String html) {
        return StringEscapeUtils.escapeHtml4(html);
    }
    /**
     * Html 解码.
     */
    public static String unescapeHtml(String htmlEscaped) {
        return StringEscapeUtils.unescapeHtml4(htmlEscaped);
    }
    /**
     * Xml 转码.
     */
    public static String escapeXml(String xml) {
        return StringEscapeUtils.escapeXml(xml);
    }
    /**
     * Xml 解码.
     */
    public static String unescapeXml(String xmlEscaped) {
        return StringEscapeUtils.unescapeXml(xmlEscaped);
    }
    /**
     * URL 编码, Encode默认为UTF-8.
     */
    public static String urlEncode(String part) {
        try {
            return URLEncoder.encode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * URL 解码, Encode默认为UTF-8.
     */
    public static String urlDecode(String part) {
        try {
            return URLDecoder.decode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}

1.1.4、StreamUtil

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/5 1:15
 * @Description:
 */
public class StreamUtil {

    public static String getRequestBody(InputStream inputStream) {
        String line = "";
        StringBuilder body = new StringBuilder();
        int counter = 0;

        // 读取POST提交的数据内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            while ((line = reader.readLine()) != null) {
                if (counter > 0) {
                    body.append("rn");
                }
                body.append(line);
                counter++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return body.toString();
    }

    public static HashMap<String, String[]> parseQueryString(String source) {
        String[] valueArray = null;
        if (source == null) {
            throw new IllegalArgumentException();
        }
        HashMap<String, String[]> hashMap = new HashMap<String, String[]>(16);
        StringTokenizer st = new StringTokenizer(source, "&");
        while (st.hasMoreTokens()) {
            String pair = (String) st.nextToken();
            int pos = pair.indexOf('=');
            if (pos == -1) {
                continue;
            }
            String key = pair.substring(0, pos);
            String val = pair.substring(pos + 1, pair.length());
            if (hashMap.containsKey(key)) {
                String[] oldValues = (String[]) hashMap.get(key);
                valueArray = new String[oldValues.length + 1];
                for (int i = 0; i < oldValues.length; i++) {
                    valueArray[i] = oldValues[i];
                }
                valueArray[oldValues.length] = decodeValue(val);
            } else {
                valueArray = new String[1];
                valueArray[0] = decodeValue(val);
            }
            hashMap.put(key, valueArray);
        }
        return hashMap;
    }

    /**
     * 自定义解码函数
     *
     * @param value
     * @return
     */
    private static String decodeValue(String value) {
        if (value.contains(WebRequestConstant.CHARACTER_U)) {
            return EncodeUtil.urlDecode(value);
        } else {
            try {
                return URLDecoder.decode(value, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // 非UTF-8编码
                return "";
            }
        }
    }

    public static ServletInputStream getServletInputStream(ByteArrayInputStream bais) {
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener arg0) {

            }
        };
    }

    public static byte[] readBytes(InputStream in) throws IOException {
        BufferedInputStream bufin = new BufferedInputStream(in);
        int buffSize = 1024;
        ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);

        byte[] temp = new byte[buffSize];
        int size = 0;
        while ((size = bufin.read(temp)) != -1) {
            out.write(temp, 0, size);
        }
        bufin.close();

        byte[] content = out.toByteArray();
        return content;
    }

}

1.1.5、MyHttpServletRequestWrapper

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/4 20:50
 * @Description:
 */
@Getter
@Setter
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    /**
     * 用于保存读取到的body中的数据
     */
    private byte[] body;
    private Map<String, String[]> paramsMap;

    @Override
    public Map getParameterMap() {
        return paramsMap;
    }

    @Override
    public String getParameter(String name) {
        String[] values = paramsMap.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        return paramsMap.get(name);
    }

    @Override
    public Enumeration getParameterNames() {
        return Collections.enumeration(paramsMap.keySet());
    }

    private HashMap<String, String[]> getParamMapFromPost(HttpServletRequest request) {

        String body = "";
        try {
            body = StreamUtil.getRequestBody(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        HashMap<String, String[]> result = new HashMap<String, String[]>(16);

        if (null == body || 0 == body.length()) {
            return result;
        }

        return StreamUtil.parseQueryString(body);
    }


    private Map<String, String[]> getParamMapFromGet(HttpServletRequest request) {
        return StreamUtil.parseQueryString(request.getQueryString());
    }

    public String getBody(){
        return new String(body);
    }

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws Exception {
        super(request);
        body = StreamUtil.readBytes(request.getInputStream());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return StreamUtil.getServletInputStream(bais);
    }

}

1.1.6、UserDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class UserDTO implements Serializable {

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

}

二、创建过滤器

2.1、MyFilter

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/4 20:37
 * @Description:
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "channelFilter")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("============= MyFilter init =============");
    }

    /**
     *      普通的参数可以通过request.getParameterMap中获取,而Controller层被@RequestBody修饰的参数需要从request的InputStream中获取,
     * 但是由于InputStream只能读取一次,如果过滤器读取了参数,那么后面的拦截器和Controller层就读取不到参数了,所以这类参数需要单独获取,可以把
     * request封装一下,copy一份request,一个用于在拦截器(过滤器)中读取参数,一个放行给Controller使用
     * 
     * @param request
     * @param response
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        ServletRequest requestWrapper = null;
        String param = "";
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String method = httpServletRequest.getMethod().toUpperCase();
            String type = httpServletRequest.getContentType();
            if (WebRequestConstant.METHOD_TYPE_POST.equals(method) && WebRequestConstant.CONTENT_TYPE_APPLICATION_JSON.equalsIgnoreCase(type)) {
                try {
                    requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        if (requestWrapper == null) {
            Map<String, String[]> originRequestMap = request.getParameterMap();
            Map<String, String> requestMap = new HashMap<String, String>(16);
            for (String key : originRequestMap.keySet()) {
                String[] values = originRequestMap.get(key);
                requestMap.put(key, values[0]);
            }
            param = JSON.toJSONString(requestMap);
        } else {
            param = ((MyHttpServletRequestWrapper) requestWrapper).getBody();
        }
        log.info("过滤器param:{}", param);
        //放行
        if (requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {
        log.info("============= MyFilter destroy =============");
    }

}

三、拦截器

3.1、创建拦截器

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/4 18:29
 * @Description: 自定义的登录拦截器
 */
@Slf4j
@Component
public class MyLoginHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse response, Object handler) throws Exception {
        log.info("============= MyLoginHandlerInterceptor preHandle =============");
        try {

            String requestUrl = httpServletRequest.getRequestURI();
            ServletRequest requestWrapper = null;
            String param = "";
            String method = httpServletRequest.getMethod().toUpperCase();
            String type = httpServletRequest.getContentType();
            if (WebRequestConstant.METHOD_TYPE_POST.equals(method) && WebRequestConstant.CONTENT_TYPE_APPLICATION_JSON.equalsIgnoreCase(type)) {
                requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest);
            }

            if (requestWrapper == null) {
                Map<String, String[]> originRequestMap = httpServletRequest.getParameterMap();
                Map<String, String> requestMap = new HashMap<String, String>(16);
                for (String key : originRequestMap.keySet()) {
                    String[] values = originRequestMap.get(key);
                    requestMap.put(key, values[0]);
                }
                param = JSON.toJSONString(requestMap);
            } else {
                param = ((MyHttpServletRequestWrapper) requestWrapper).getBody();
            }
            log.info("拦截器param:{}", param);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("============= MyLoginHandlerInterceptor postHandle =============");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("============= MyLoginHandlerInterceptor afterCompletion =============");
    }
}

3.2、注册拦截器

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/4 18:37
 * @Description:
 */
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    /**
     * 引入自定义的拦截器对象
     */
    @Resource
    private MyLoginHandlerInterceptor loginHandlerInterceptor;

    /**
     * 注册拦截器
     * @param registry
     *      addPathPatterns("/**"):拦截所有
     *      excludePathPatterns("/business/*"):放行以/business打头的请求
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginHandlerInterceptor).addPathPatterns("/**").excludePathPatterns("/business/*").order(1);
        // registry.addInterceptor(loginHandlerInterceptor).addPathPatterns("/**");
    }
}

四、使用拦截器(案例)

4.1、获取Controller层@RequestBody中的对象参数

4.1.1、LoginController#login()

@PostMapping("/login")
public String login(@RequestBody UserDTO param) {
    log.info("LoginController login param:{}", param);
    return "OK";
}

4.1.2、测试

4.2、获取Controller层普通的对象参数

4.2.1、LoginController#login2()

@PostMapping("/login2")
public String login2(UserDTO param) {
    log.info("LoginController login2 param:{}", param);
    return "OK";
}

4.2.2、测试#form-data

4.2.3、测试#x-www-form-urlencoded

4.3、获取Controller层字符串参数

4.3.1、LoginController#login3()

@GetMapping("/login3")
public String login3(String username,String password) {
    log.info("LoginController login3 param username:{},password:{}", username,password);
    return "OK";
}

4.3.2、测试

五、参考

https://blog.csdn.net/w_t_y_y/article/details/103407841

https://blog.csdn.net/weixin_45100881/article/details/128660421

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

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

相关文章

多模态最新经典论文合集,涵盖预训练、表征学习、多模态融合

最近多模态相关的论文好火&#xff0c;原因就不多说了&#xff08;懂得都懂&#xff09;&#xff0c;因为有不少想发paper的同学来问了&#xff0c;我就火速整理了一部分来和你们分享。 这次整理了6篇最新的多模态论文&#xff0c;还有12篇经典的文章&#xff0c;主要涉及预训…

优先级队列(堆)的概念+模拟堆的实现

文章目录 优先级队列&#xff08;堆&#xff09;的概念模拟堆的实现一、概念1.优先级队列2.堆1.堆的性质2.堆的存储3.堆的创建3.1 向下调整3.2建堆的时间复杂度 O(N) 4.堆的插入4.1向上调整4.2向上调整建堆的时间复杂度&#xff1a;O(N * log N) 5.堆的删除 优先级队列&#xf…

【Java 进阶篇】Java Session 原理及快速入门

大家好&#xff0c;欢迎来到本篇博客。今天&#xff0c;我们将探讨Java Web开发中一个重要而令人兴奋的概念&#xff0c;即Session&#xff08;会话&#xff09;。Session是一种在Web应用程序中跟踪用户状态和数据的机制。我们将深入了解Session的原理&#xff0c;并通过示例来…

LangChain+LLM实战---LlamaIndex、正确使用索引

LlamaIndex简介 LlamaIndex(也称为GPT Index)是一个用户友好的界面&#xff0c;它将您的外部数据连接到大型语言模型(Large Language Models, llm)。它提供了一系列工具来简化流程&#xff0c;包括可以与各种现有数据源和格式(如api、pdf、文档和SQL)集成的数据连接器。此外&a…

Android Studio(对话框AlertDialog)

前言 前面介绍了常用控件的相关属性&#xff0c;那些控件的使用起来也很容易。在本节及后面的章节介绍的控件将是相比于前面使用起来较为复杂的&#xff08;不过使用多了&#xff0c;也很容易上手&#xff09;。 这些控件常常需要配合java代码来使用&#xff0c;比如说对话框、…

如何将PDF文件转换成翻页电子书?这个网站告诉你

​随着电子书的普及&#xff0c;越来越多的人开始将PDF文件转换成翻页电子书。翻页电子书不仅方便阅读&#xff0c;而且还可以在手机上轻松翻页。那么如何将PDF文件转换成翻页电子书呢&#xff1f;今天就为大家介绍一个网站&#xff0c;可以帮助你轻松完成这个任务。 1.首先&am…

Redis-使用java代码操作Redis->java连接上redis,java操作redis的常见类型数据存储,redis中的项目应用

java连接上redisjava操作redis的常见类型数据存储redis中的项目应用 1.java连接上redis package com.zlj.ssm.redis;import redis.clients.jedis.Jedis;/*** author zlj* create 2023-11-03 19:27*/ public class Demo1 {public static void main(String[] args) { // …

语言的新启程之Solidity

官方网站 WTF-Solidity官网 编译器 区块链的基础 Gas 一经创建&#xff0c;每笔交易都收取一定数量的 gas&#xff0c;目的是限制执行交易所需要的工作量和为交易支付手续费。EVM 执行交易时&#xff0c;gas 将按特定规则逐渐耗尽。 gas price 是交易发送者设置的一个值&…

深入理解计算机系统CS213 - Lecture 02

Bits, Bytes, and Integer 1.位运算与条件运算 &&#xff0c;|&#xff0c;^&#xff0c;~ 是做位运算。诸位01运算。 &&&#xff0c;||&#xff0c;&#xff01;是判断条件真假&#xff0c;而后返回0或1。 2. 位移 x << y&#xff1a;左移y位&#xff…

Excel·VBA工作表导出为图片

《Excel转图片别再截图啦&#xff01;用这4个方法&#xff0c;高清且无损&#xff01;》&#xff0c;excel转为图片一般方法较为简单&#xff0c;那么能否使用vba将excel转为图片 选中区域导出为图片 zoom设置为2&#xff0c;导出图片较为清晰 Sub 选中区域导出为图片()Dim …

【iOS】——知乎日报第三周总结

文章目录 一、获取新闻额外信息二、工具栏按钮的布局三、评论区文字高度四、评论区长评论和短评论的数目显示五、评论区的cell布局问题和评论消息的判断 一、获取新闻额外信息 新闻额外信息的URL需要通过当前新闻的id来获取&#xff0c;所以我将所有的新闻放到一个数组中&…

MySQL基础『数据库基础』

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; MySQL 学习 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 &#x1f381;软件版本&#xff1a; MySQL 5.7.44 文章目录 1.数据库概念1.1.什么是数据库1.2.数据库存储介质1.3.常见数据库 2.数…

【带头学C++】----- 三、指针章 ---- 3.5 字符串与指针

在 C 中&#xff0c;字符串可以通过指针来表示和操作。C 的字符串是由字符组成的字符数组&#xff0c;而指针则用于引用和操作内存中的数据。 1. 字符数组 1. 字符数组: 字符数组是最基本的字符串表示方式。可以使用字符数组来存储字符串&#xff0c;并使用指针来引用它。字符…

嵌入式中利用VS Code 远程开发原理

VS Code几乎是所有的程序员必备的工具之一&#xff0c;据说全球一般的开发者都使用过VS Code这款工具。 今天分享一篇 VS Code 实现远程办公相关的文章。 1、概 述 通常&#xff0c;我们都是每天到工作的办公室进行办公&#xff0c;但是&#xff0c;如果下班回家&#x…

Linux学习第32天:Linux INPUT 子系统实验(一):接纳

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 题目中用了“接纳”俩字。其实学习就是一个接纳的过程。接纳新的知识&#xff0c;从而转化为自己知识宝库的一部分。那今天学习的input子系统和今天的主题接纳有…

0基础2小时搭建自己的网站

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;0基础2小时搭建个人网站 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;宁静是一片强大而治愈的神奇海洋&#xff01; 目录 前言 第一步 环境…

DETR 论文精读【论文精读】End-to-End Object Detection with Transformers

DETR 这篇论文&#xff0c;大家为什么喜欢它&#xff1f;为什么大家说它是一个目标检测里的里程碑式的工作&#xff1f;而且为什么说它是一个全新的架构&#xff1f; 大家好&#xff0c;今天我们来讲一篇 ECC V20 的关于目标检测的论文。它的名字想必大家都不陌生&#xff0c;也…

系列十二、过滤器 vs 拦截器

一、过滤器 vs 拦截器 1.1、区别 &#xff08;1&#xff09;触发时机不一样&#xff0c;过滤器是在请求进入容器后Servlet之前进行预处理的&#xff0c;请求结束返回也是&#xff0c;是在Servlet处理完后&#xff0c;返回给前端之前&#xff1b; &#xff08;2&#xff09;过滤…

5.3有效的括号(LC20-E)

算法&#xff1a; 题目中&#xff1a;左括号必须以正确的顺序闭合。意思是&#xff0c;最后出现的左括号&#xff08;对应着栈中的最后一个元素&#xff09;&#xff0c;应该先找到对应的闭合符号&#xff08;右括号&#xff09; 比如:s"( [ ) ]"就是False&#xf…

个人服务器到期,项目下线,新的开始

告别旧服务器 2023.11.06服务器到期&#xff0c;所有项目正式下线 时间真的过的很快&#xff0c;从开始踏入编程的大门&#xff0c;到现在不知不觉已经陆续经手了两台服务器了&#xff0c;目前这台服务器是一年前的阿里云活动白嫖的嘿嘿嘿&#xff0c;该服务器上目前运行的项…