基于Springboot+netty 的IM聊天室弹幕

news2024/9/20 20:51:39

代码地址

仓库地址
image.png

聊天室

创建springboot项目

image.png

image.png

因为我的NettyServer 是8080 所以我把服务端口改为了8081

image.png

引入netty 依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.92.Final</version>
        </dependency>

IMServerHandler 处理用户连接以及发送消息等功能的逻辑

package site.zhourui.netty.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zr 2024/8/13
 */
public class IMServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static final Map<Channel, String> userNicknames = new HashMap<>();
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
        String text = frame.text();
        Map<String, String> data = objectMapper.readValue(text, HashMap.class);
        String type = data.get("type");

        if ("set_nickname".equals(type)) {
            String nickname = data.get("nickname");
            userNicknames.put(ctx.channel(), nickname);
            System.out.println("User set nickname: " + nickname);

            // 广播用户加入消息
            broadcastMessage("系统", "用户 " + nickname + " 加入聊天");
        } else if ("chat_message".equals(type)) {
            String message = data.get("message");
            String nickname = userNicknames.get(ctx.channel());

            // 避免 nickname 未设置的情况
            if (nickname == null) {
                nickname = "Anonymous";
                userNicknames.put(ctx.channel(), nickname);
            }

            // 广播聊天消息
            broadcastMessage(nickname, message);
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("新用户连接: " + ctx.channel().id().asShortText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String nickname = userNicknames.remove(ctx.channel());
        if (nickname != null) {
            System.out.println("User disconnected: " + nickname);
            // 广播用户离开消息
            broadcastMessage("系统", "用户 " + nickname + " 离开聊天");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    private void broadcastMessage(String senderNickname, String message) {
        for (Channel channel : userNicknames.keySet()) {
            try {
                Map<String, String> response = new HashMap<>();
                response.put("nickname", senderNickname);
                response.put("message", message);
                channel.writeAndFlush(new TextWebSocketFrame(objectMapper.writeValueAsString(response)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


NettyServer 聊天室服务端

package site.zhourui.netty.server;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import site.zhourui.netty.handler.IMServerHandler;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zr 2024/8/13
 */
public class NettyServer {

    private final int port;

    // 存储所有连接的用户
    public static final ConcurrentHashMap<String, Channel> userChannels = new ConcurrentHashMap<>();

    public NettyServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HttpServerCodec());
                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            ch.pipeline().addLast(new HttpObjectAggregator(8192));
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws"));
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new StringEncoder());
                            ch.pipeline().addLast(new IMServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(port).sync();
            System.out.println("Netty IM server started on port " + port);
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    // 记录用户连接
    public static void addUser(String userId, Channel channel) {
        userChannels.put(userId, channel);
    }

    // 获取用户连接
    public static Channel getUserChannel(String userId) {
        return userChannels.get(userId);
    }

    // 移除用户连接
    public static void removeUser(String userId) {
        userChannels.remove(userId);
    }
}

将NettyServer 标记为组件,随springboot启动

package site.zhourui.netty.component;


import org.springframework.stereotype.Component;
import site.zhourui.netty.server.NettyServer;

import javax.annotation.PostConstruct;

/**
 * @author zr 2024/8/13
 */
@Component
public class NettyServerComponent {
    @PostConstruct
    public void startNettyServer() {
        Thread nettyThread = new Thread(() -> {
            try {
                new NettyServer(8080).run();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        nettyThread.setDaemon(true);  // 设置为守护线程
        nettyThread.start();
    }
}

chatClient.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket IM Client</title>
    <style>
      #chat-container {
        width: 50%;
        margin: auto;
        margin-top: 50px;
        border: 1px solid #ccc;
        padding: 20px;
        border-radius: 5px;
      }

      #messages {
        height: 300px;
        overflow-y: scroll;
        border: 1px solid #ccc;
        padding: 10px;
        margin-bottom: 20px;
      }

      #message-input {
        width: 80%;
        padding: 10px;
        border-radius: 5px;
        border: 1px solid #ccc;
      }

      #send-button {
        padding: 10px 20px;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
      }

      #send-button:hover {
        background-color: #45a049;
      }

      /* 样式定义弹窗 */
      #config-modal {
        display: none;
        position: fixed;
        z-index: 1;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
      }

      #config-form {
        background-color: #fff;
        margin: 15% auto;
        padding: 20px;
        border: 1px solid #ccc;
        width: 300px;
        border-radius: 5px;
      }

      #config-form input {
        width: 100%;
        padding: 10px;
        margin-bottom: 10px;
        border: 1px solid #ccc;
        border-radius: 5px;
      }

      #config-form button {
        width: 100%;
        padding: 10px;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
      }

      #config-form button:hover {
        background-color: #45a049;
      }
    </style>
  </head>
  <body>
    <div id="config-modal">
      <div id="config-form">
        <input type="text" id="server-address" placeholder="Server Address" value="ws://localhost">
        <input type="text" id="server-port" placeholder="Port" value="8080">
        <input type="text" id="nickname" placeholder="Nickname">
        <button id="connect-button">Connect</button>
      </div>
    </div>

    <div id="chat-container">
      <h1>WebSocket IM Client</h1>
      <div id="messages"></div>
      <input type="text" id="message-input" placeholder="Type your message here..." />
      <button id="send-button">Send</button>
    </div>

    <script>
      let socket;
      let nickname = null;

      // 显示配置对话框
      const modal = document.getElementById('config-modal');
      modal.style.display = 'block';

      document.getElementById('connect-button').addEventListener('click', function() {
                                                                 const serverAddress = document.getElementById('server-address').value;
            const serverPort = document.getElementById('server-port').value;
            nickname = document.getElementById('nickname').value;

            if (!serverAddress || !serverPort || !nickname) {
                alert("All fields are required!");
                return;
            }

            // 创建 WebSocket 连接
            socket = new WebSocket(`${serverAddress}:${serverPort}/ws`);

            // 连接打开事件
            socket.onopen = function() {
                console.log('Connected to IM Server');
                socket.send(JSON.stringify({ type: 'set_nickname', nickname: nickname }));
                modal.style.display = 'none'; // 连接成功后隐藏配置对话框
            };

            // 处理收到的消息
            socket.onmessage = function(event) {
                const data = JSON.parse(event.data);
                const senderNickname = data.nickname;
                const message = data.message;

                const messageDiv = document.createElement('div');
                if (senderNickname === nickname) {
                    messageDiv.textContent = `me: ${message}`;
                    messageDiv.style.color = 'blue'; // 给自己的消息加一个样式
                } else {
                    messageDiv.textContent = `${senderNickname}: ${message}`;
                }
                document.getElementById('messages').appendChild(messageDiv);
                document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight; // 自动滚动到底部
            };

            // 连接关闭事件
            socket.onclose = function() {
                console.log('Disconnected from IM Server');
            };
        });

        // 发送消息
        document.getElementById('send-button').addEventListener('click', function() {
            const message = document.getElementById('message-input').value.trim();
            if (message) {
                socket.send(JSON.stringify({ type: 'chat_message', message: message }));
                document.getElementById('message-input').value = ''; // 清空输入框
            }
        });

        // 支持按 Enter 键发送消息
        document.getElementById('message-input').addEventListener('keypress', function(event) {
            if (event.key === 'Enter') {
                document.getElementById('send-button').click();
            }
        });
    </script>
</body>
</html>

项目结构

image.png

  1. 浏览器打开chatClient.html 第一个用户设置昵称为111

image.png

  1. 加入聊天室后即可接收到消息

image.png

  1. 浏览器再次打开chatClient.html 第二个用户设置昵称为222

image.png

  1. 222用户显示

image.png

  1. 111用户显示:222用户也加入聊天

image.png

  1. 111发送消息

image.png

  1. 222收到消息

image.png

弹幕

弹幕和聊天室服务器目前是一样的,只是客户端有部分不同

barrageClient.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket IM Client with Stylish Danmaku</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background-image: url('https://example.com/your-background.jpg'); /* 替换为你想要的背景图 */
            background-size: cover;
            background-position: center;
            height: 100vh;
            font-family: Arial, sans-serif;
            color: white;
        }

        #chat-container {
            width: 70%;
            height: 80%;
            margin: auto;
            margin-top: 5%;
            padding: 20px;
            border-radius: 10px;
            position: relative;
            overflow: hidden; /* 确保弹幕不会超出容器 */
            background: rgba(0, 0, 0, 0.6); /* 半透明黑色背景 */
        }

        .danmaku-message {
            position: absolute;
            white-space: nowrap;
            font-size: 18px;
            padding: 5px 10px;
            border-radius: 5px;
            color: #fff;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
            animation: danmaku-move 7s linear infinite;
            background: rgba(0, 0, 0, 0.5); /* 半透明背景 */
        }

        @keyframes danmaku-move {
            from {
                left: 100%; /* 从屏幕右侧外开始 */
            }
            to {
                left: -100%; /* 移动到屏幕左侧外 */
            }
        }

        #message-input {
            width: 70%;
            padding: 15px;
            border-radius: 25px;
            border: 1px solid #ccc;
            background-color: rgba(255, 255, 255, 0.8);
            color: #333;
        }

        #send-button {
            padding: 15px 25px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 25px;
            cursor: pointer;
            margin-left: 10px;
            font-size: 16px;
        }

        #send-button:hover {
            background-color: #45a049;
        }

        #config-modal {
            display: none;
            position: fixed;
            z-index: 1;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
        }

        #config-form {
            background-color: #fff;
            margin: 15% auto;
            padding: 20px;
            border: 1px solid #ccc;
            width: 300px;
            border-radius: 10px;
        }

        #config-form input {
            width: 100%;
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 25px;
        }

        #config-form button {
            width: 100%;
            padding: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 25px;
            cursor: pointer;
        }

        #config-form button:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <div id="config-modal">
        <div id="config-form">
            <input type="text" id="server-address" placeholder="Server Address" value="ws://localhost">
            <input type="text" id="server-port" placeholder="Port" value="8080">
            <input type="text" id="nickname" placeholder="Nickname">
            <button id="connect-button">Connect</button>
        </div>
    </div>

    <div id="chat-container">
        <h1>WebSocket IM Client</h1>
        <input type="text" id="message-input" placeholder="Type your message here..." />
        <button id="send-button">Send</button>
    </div>

    <script>
        let socket;
        let nickname = null;

        // 显示配置对话框
        const modal = document.getElementById('config-modal');
        modal.style.display = 'block';

        document.getElementById('connect-button').addEventListener('click', function() {
            const serverAddress = document.getElementById('server-address').value;
            const serverPort = document.getElementById('server-port').value;
            nickname = document.getElementById('nickname').value;

            if (!serverAddress || !serverPort || !nickname) {
                alert("All fields are required!");
                return;
            }

            // 创建 WebSocket 连接
            socket = new WebSocket(`${serverAddress}:${serverPort}/ws`);

            // 连接打开事件
            socket.onopen = function() {
                console.log('Connected to IM Server');
                socket.send(JSON.stringify({ type: 'set_nickname', nickname: nickname }));
                modal.style.display = 'none'; // 连接成功后隐藏配置对话框
            };

            // 处理收到的消息
            socket.onmessage = function(event) {
                const data = JSON.parse(event.data);
                const senderNickname = data.nickname;
                const message = data.message;

                const messageDiv = document.createElement('div');
                messageDiv.classList.add('danmaku-message');
                if (senderNickname === nickname) {
                    messageDiv.textContent = `me: ${message}`;
                    messageDiv.style.color = 'lightblue'; // 给自己的消息加一个样式
                } else {
                    messageDiv.textContent = `${senderNickname}: ${message}`;
                }

                const chatContainer = document.getElementById('chat-container');
                messageDiv.style.top = `${Math.random() * (chatContainer.clientHeight - 20)}px`; // 随机设置弹幕的高度
                chatContainer.appendChild(messageDiv);

                // 删除弹幕元素以避免占用内存
                setTimeout(() => {
                    chatContainer.removeChild(messageDiv);
                }, 7000); // 弹幕持续7秒
            };

            // 连接关闭事件
            socket.onclose = function() {
                console.log('Disconnected from IM Server');
            };
        });

        // 发送消息
        document.getElementById('send-button').addEventListener('click', function() {
            const message = document.getElementById('message-input').value.trim();
            if (message) {
                socket.send(JSON.stringify({ type: 'chat_message', message: message }));
                document.getElementById('message-input').value = ''; // 清空输入框
            }
        });

        // 支持按 Enter 键发送消息
        document.getElementById('message-input').addEventListener('keypress', function(event) {
            if (event.key === 'Enter') {
                document.getElementById('send-button').click();
            }
        });
    </script>
</body>
</html>

  1. 浏览器打开chatClient.html 第一个用户设置昵称为1,第二个用户设置昵称为2

image.png

  1. 加入时用户提醒

image.png

  1. 用户1发送消息

image.png

  1. 用户2发送消息

image.png

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

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

相关文章

Prometheus+Grafana保姆笔记(1)——Prometheus+Grafana的安装

Prometheus Grafana 的组合在微服务项目中可以完成许多DevOps任务&#xff0c;它们共同提供了强大的监控和可视化功能。 我们陆续介绍Prometheus Grafana 的相关用法。 首先介绍PrometheusGrafana的安装。 安装 Prometheus Prometheus 是GO写的&#xff0c;并不依赖于 Ja…

【深度学习】注意力机制(Transformer)

注意力机制 1.基础概念 1.1 查询、键和值 在人类的注意力方式中&#xff0c;有自主性的与非自主性的注意力提示两种解释方式。所谓自主性注意力提示&#xff0c;就是人本身主动想要关注到的某样东西&#xff1b;非自主性提示则是基于环境中物体的突出性和易见性&#xff0c;…

8.13面试题目

美团商业分析实习生 一、8个球有一个重一点&#xff0c;最少称几次找出 2次 8个球中有一个重一点&#xff0c;最少称2次能找出来。 具体称重步骤如下&#xff1a; 第一次称重&#xff1a;将8个球分成三组&#xff0c;分别为3个球、3个球和2个球。将两组各3个球放在天平的两端…

【搜索二维矩阵】python刷题记录

R4-二分查找专题 直接二维变一维&#xff0c;然后二分查找就可以了 class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:nums[i for row in matrix for i in row]def binfind(the,target):low,high0,len(the)-1while low<high:mid(l…

猫罐头挑选指南!猫咪肥胖有什么危害?健康主食罐挑选攻略!

今天有位铲屎官带着一只17斤的布偶来我们医院。猫猫三岁了&#xff0c;绝育两年&#xff0c;现在因为太胖了&#xff0c;舔毛都不太方便。铲屎官说它平时不太爱动。我给体检后发现甘油三酯高&#xff0c;其他都正常&#xff0c;但必须开始减肥了&#xff0c;猫咪太胖可不好。 一…

江协科技STM32学习笔记

第01章 STM32简介及开发环境搭建 1.1 STM32简介 1.1.1 STM32F103C8T6 系列&#xff1a;主流系列STM32F1 内核&#xff1a;ARM Cortex-M3 主频&#xff1a;72MHz RAM&#xff1a;20K&#xff08;SRAM&#xff09; ROM&#xff1a;64K&#xff08;Flash&#xff09; 供电…

开学季好物分享!全能抗打运动耳机!

大家好&#xff01;开学季来临&#xff0c;在学校的时候&#xff0c;一些运动和体育项目是在所难免的&#xff0c;但是在运动过程中肯定是枯燥无味的&#xff0c;所以这时候就需要一款合适的运动耳机陪伴我们。作为一名对运动耳机有较高要求的学生&#xff0c;我最近发现了一款…

配置jconsole远程监控

# 启动命令 -Djava.rmi.server.hostname192.168.0.67 #java程序所在机器ip -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port8011 #jconsole端口 -Dcom.sun.management.jmxremote.authenticatetrue # 配置为false 就不需要后面的password和access配置 -D…

普元EOS-用户、角色、权限的设计思路

1 前言 普元EOS作为企业应用开发&#xff0c;肯定要对用户、角色、权限这些内容进行管理&#xff0c;本文就描述这些内容EOS的设计思想&#xff0c;以及如何使用这些工具。 2 传统的访问权限控制 先说一个RBAC的概念&#xff0c;就是 Role-Based Access Control&#xff0c;中…

Java Web —— 第五天(请求响应2)

响应数据 ResponseBody 类型:方法注解、类注解 位置: Controller方法上/类上 作用:将方法返回值直接响应&#xff0c;如果返回值类型是 实体对象/集合&#xff0c;将会转换为JSON格式响应 说明: RestController Controller ResponseBody ; package com.example.springbo…

嘉实基金“青黄不接”:董事长赵学军被调查,明星经理基金绿油油

尚未实现“打造中国版的贝莱德”之梦&#xff0c;手握万亿资产管理规模的嘉实基金便“出事”了。 近日&#xff0c;嘉实基金在官网披露了一则临时公告&#xff0c;称其于2024年8月9日从相关方面获悉&#xff0c;该公司董事长赵学军因个人问题正配合有关部门调查。目前&#xf…

录音怎么转换成mp3格式?6款免费音频转换mp3的软件大公开!(赶紧收藏)

录音怎么转换成mp3格式&#xff1f;录音是我们日常记录生活和工作的常见方式之一。然而&#xff0c;不同的音频格式往往会带来兼容性和播放上的困难。如果您曾因为无法播放某个录音而烦恼&#xff0c;或者希望能够方便地将录音转换成mp3格式&#xff0c;那么本文就能告诉您答案…

那些年我们一起遇到过的奇技淫巧

EVAL长度限制突破技巧 PHP Eval函数参数限制在16个字符的情况下 &#xff0c;如何拿到Webshell&#xff1f; 写一段限制长度在小于17位的字符&#xff0c;拿下webshell <?php highlight_file(__FILE__); $param $_REQUEST[param]; if (strlen($param) < 17 &&am…

【安卓】播放多媒体文件

文章目录 播放音频播放视频 播放音频 在Android中播放音频文件一般是使用MediaPlayer类实现的&#xff0c;它对多种格式的音频文件提供了非常全面的控制方法&#xff0c;从而使播放音乐的工作变得十分简单。 MediaPlayer类中常用的控制方法。 常用方法名描述setDataSource()设…

合作文章(IF=14.3)|RNA-Seq和ChIP-Seq助力SOX12调节肝癌细胞的机制研究以及肝癌联合治疗的免疫策略的发现

肝细胞癌(HCC)是全球严重的健康负担。长期以来&#xff0c;HCC的治疗选择很少&#xff0c;但免疫检查点阻断(ICB)近年来取得了重大突破。在肿瘤免疫微环境(TIME)中&#xff0c;免疫细胞通过配对的免疫检查点或其他信号与肿瘤细胞相互作用&#xff0c;形成异质的免疫抑制生态系统…

AI高级肖像动画神器LivePortrait

文章目录 前言一、安装1.1 源码安装1.2 windows一键启动包 二、人像生成2.1 浏览器2.2 输入图像2.3 选择驱动视频2.4 生成2.5 结果 三、动物生成3.1 浏览器3.2 输入图片3.3 选择视频3.4 生成3.5 最终结果 四、软件获取 前言 最近&#xff0c;快手可灵大模型团队、中国科学技术…

go 事件机制(观察者设计模式)

背景&#xff1a; 公司目前有个业务&#xff0c;收到数据后&#xff0c;要分发给所有的客户端或者是业务模块&#xff0c;类似消息通知这样的需求&#xff0c;自然而然就想到了事件&#xff0c;观察者比较简单就自己实现以下&#xff0c;确保最小功能使用支持即可&#xff0c;其…

LeetCode旋转图像

题目描述&#xff1a; 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3]…

100 Exercises To Learn Rust 挑战!构文・整数・变量

前一篇文章 【0】准备 【1】构文・整数・变量 ← 本次全部文章列表 《100 Exercise To Learn Rust》第2回&#xff0c;也就是实际演习的第1回&#xff01;从这次开始&#xff0c;我们会适度减少前置说明&#xff0c;直接进入问题的解决&#xff01; 本次的相关页面 1.1. Syn…

VUE3请求意外报跨越错误或者500错误问题

1.有可能是请求传参和传参类型写错了 首先要确保该请求接口是支持跨域的&#xff08;不支持叫后端改&#xff09; access-control-allow-headers:Content-Type, Accept, Access-Control-Allow-Origin, api_key, Authorization access-control-allow-methods:GET, POST, OPTIO…