2024-09-09



// 在Spring Boot的Controller中添加登录接口
@RestController
public class AuthController {
 
    @Autowired
    private AuthenticationManager authenticationManager;
 
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        try {
            // 使用Spring Security提供的AuthenticationManager进行认证
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(), 
                    loginRequest.getPassword()
                )
            );
            SecurityContextHolder.getContext().setAuthentication(authentication);
            // 生成并返回JWT令牌
            String token = Jwts.builder()
                .setSubject(loginRequest.getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + 864000000))
                .signWith(SignatureAlgorithm.HS512, "YourSecretKey")
                .compact();
            return ResponseEntity.ok(new JwtResponse(token));
        } catch (AuthenticationException e) {
            return new ResponseEntity<>(Collections.singletonMap("error", e.getMessage()), HttpStatus.UNAUTHORIZED);
        }
    }
}
 
// Vue.js中发送登录请求并处理响应
export default {
    data() {
        return {
            username: '',
            password: ''
        };
    },
    methods: {
        login() {
            axios.post('http://localhost:8080/login', {
                username: this.username,
                password: this.password
            })
            .then(response => {
                localStorage.setItem('token', response.data.token);
                // 登录成功后的操作,例如跳转到主页
                this.$router.push('/');
            })
            .catch(error => {
                console.error('登录失败', error);
                // 登录失败的操作,例如显示错误信息
            });
        }
    }
}

这个简易的例子展示了如何在Spring Boot后端使用AuthenticationManager进行用户认证,并在成功认证后生成JWT令牌。在Vue.js前端,用户提交登录信息,后端返回JWT令牌后,将其保存在localStorage中,并且可以根据实际需求进行页面跳转或错误处理。

2024-09-09

Redis 的 List 数据结构是一个字符串链表,你可以在这个链表的头部或尾部添加或删除元素。这使得 List 既可以作为栈,也可以作为队列使用。

以下是 Redis List 数据结构的常用命令:

  • LPUSH key value[ value ...]:在 key 对应的 list 的头部添加元素,可以添加一个或多个元素。
  • RPUSH key value[ value ...]:在 key 对应的 list 的尾部添加元素,可以添加一个或多个元素。
  • LRANGE key start stop:返回 key 对应的 list 中指定区间内的元素,区间通过 start 和 stop 指定,-1 表示最后一个元素。
  • LPOP key:移除并返回 key 对应的 list 的头部元素。
  • RPOP key:移除并返回 key 对应的 list 的尾部元素。

实例代码:




import redis
 
# 连接 Redis
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 在 list 头部添加元素
r.lpush('mylist', 'element1')
r.lpush('mylist', 'element2', 'element3')
 
# 在 list 尾部添加元素
r.rpush('mylist', 'element4')
r.rpush('mylist', 'element5', 'element6')
 
# 获取 list 中的所有元素
print(r.lrange('mylist', 0, -1))  # 输出:['element6', 'element5', 'element4', 'element3', 'element2', 'element1']
 
# 移除并返回 list 的头部元素
print(r.lpop('mylist'))  # 输出:element6
 
# 移除并返回 list 的尾部元素
print(r.rpop('mylist'))  # 输出:element1

以上代码演示了如何在 Python 中使用 redis 模块操作 Redis 的 List 数据结构。

2024-09-09

在Spring Boot中,对象的序列化和反序列化通常是通过使用Jackson库来实现的。Jackson是Spring Boot默认的JSON处理库。

以下是一个简单的例子,演示如何在Spring Boot应用程序中序列化和反序列化一个对象。

首先,定义一个简单的实体类:




import com.fasterxml.jackson.annotation.JsonProperty;
 
public class User {
    @JsonProperty
    private String name;
    @JsonProperty
    private int age;
 
    // 构造函数、getter和setter省略
}

序列化(对象转JSON字符串):




import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class SerializationExample {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("Alice", 30);
 
        try {
            String json = mapper.writeValueAsString(user);
            System.out.println(json);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

反序列化(JSON字符串转对象):




import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class DeserializationExample {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        String json = "{\"name\":\"Bob\",\"age\":25}";
 
        try {
            User user = mapper.readValue(json, new TypeReference<User>() {});
            System.out.println(user.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们创建了一个User对象,并使用Jackson的ObjectMapper将其序列化为JSON字符串。然后,我们将JSON字符串反序列化回User对象。

注意:确保你的项目中已经包含了Jackson的依赖。如果你使用的是Spring Boot,那么通常这个依赖会自动包含在内。

2024-09-09

Spring Boot整合SSE(Server-Sent Events)的方法如下:

  1. 添加依赖:确保你的pom.xml中包含Spring Web依赖。



<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 创建SSE控制器:



import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
@RestController
public class SseController {
 
    @GetMapping(path = "/stream-sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter handleSse() {
        SseEmitter emitter = new SseEmitter();
 
        // 在新线程中发送事件
        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    emitter.send("data:" + i + "\n\n"); // 发送数据
                    Thread.sleep(1000); // 每秒发送一次
                }
                emitter.complete(); // 完成发送
            } catch (Exception e) {
                emitter.completeWithError(e); // 发送错误
            }
        }).start();
 
        return emitter;
    }
}
  1. 测试:启动Spring Boot应用,并通过浏览器或者使用curl命令行工具来测试SSE端点。

在浏览器中打开:




http://localhost:8080/stream-sse

使用curl测试:




curl -N http://localhost:8080/stream-sse

以上代码创建了一个简单的SSE服务,当客户端连接到/stream-sse端点时,它会收到一系列数字,每秒更新一次。这只是SSE用法的简单示例,实际应用中可能需要更复杂的逻辑。

2024-09-09

在Redis中,使用SCAN命令可以进行百万级的键模糊查找。SCAN命令是以非阻塞的方式迭代当前数据库中的数据库键。

SCAN命令的基本语法如下:




SCAN cursor [MATCH pattern] [COUNT count]
  • cursor:迭代的游标,初始时以0开始,迭代过程中需要使用上一次返回的游标继续进行迭代。
  • MATCH pattern:可选参数,用于指定匹配的模式。
  • COUNT count:可选参数,提供每次迭代返回的近似键数,默认值通常为10。

SCAN命令返回两个元素,第一个是新的游标,第二个是匹配的键列表。当返回的游标为0时,表示迭代结束。

KEYS命令相比,SCAN命令的优势在于它不会阻塞服务器,使用SCAN命令可以逐步遍历大数据集合。

KEYS命令在处理大数据集时应谨慎使用,因为它可能会导致服务器阻塞。

下面是一个使用SCAN命令进行模糊查找的例子:




# 假设我们要查找所有以"user:"开头的键

# 初始化游标为0
scan 0 MATCH user:* COUNT 100

# 假设返回的游标为"42",则继续迭代
scan 42 MATCH user:* COUNT 100

# 继续,直到游标返回0,表示遍历完成

请注意,COUNT选项只是一个提示,返回的键数可能会超过或少于指定的COUNT值。使用SCAN命令时,应该保持一个循环,使用新的游标值继续迭代,直至游标值为0。

2024-09-09



-- 创建服务器对象,用于连接到外部S3 bucket
CREATE SERVER s3_server
    FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (
        protocol 's3',
        access_key 'your_access_key',
        secret_access_key 'your_secret_access_key',
        region 'us-west-2'
    );
 
-- 创建外部表,映射到S3上的Parquet文件
CREATE FOREIGN TABLE parquet_table (
    id INT,
    name TEXT,
    value DECIMAL(10, 2)
) SERVER s3_server
OPTIONS (
    format 'PARQUET',
    filename 'your_bucket_name/folder_name/file_name.parquet'
);
 
-- 查询外部Parquet表中的数据
SELECT * FROM parquet_table;

这个示例代码展示了如何在PostgreSQL中通过使用Foreign Data Wrapper (FDW) 来连接并查询存储在S3上的Parquet文件。首先,创建了一个服务器对象来定义连接到S3所需的参数,然后创建了一个外部表来指定Parquet文件的位置和模式。最后,可以使用普通的SQL查询来访问这些数据。这个例子需要在PostgreSQL中安装并启用postgres\_fdw扩展,并且需要有S3的访问权限以及相应的Parquet文件。

2024-09-09



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {
 
    @Autowired
    private StringRedisTemplate redisTemplate;
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        if (redisTemplate.opsForValue().get(token) != null) {
            // Token存在,表示重复提交
            return false;
        } else {
            // Token不存在,表示第一次提交,存入Redis并设置过期时间
            redisTemplate.opsForValue().set(token, "1", 10, TimeUnit.MINUTES);
            return true;
        }
    }
}

这段代码实现了一个简单的拦截器,用于检测请求是否为重复提交。如果请求的header中包含token,并且Redis中已经存在这个token,就认为是重复提交,返回false拦截请求。如果Redis中不存在这个token,就将它添加到Redis中,并设置合适的过期时间,从而允许请求通过,并认为这是第一次提交。这样可以有效地防止用户因为网络问题而导致的重复提交,增加了用户体验。

2024-09-09



from rest_framework import serializers
from myapp.models import Author, Book
 
class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'  # 或者明确列出字段
 
class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer()  # 嵌套序列化Author信息
 
    class Meta:
        model = Book
        fields = '__all__'  # 或者明确列出字段
 
    def create(self, validated_data):
        author_data = validated_data.pop('author')
        author = Author.objects.create(**author_data)
        book = Book.objects.create(author=author, **validated_data)
        return book
 
    def update(self, instance, validated_data):
        author_data = validated_data.pop('author')
        author = instance.author
        author.name = author_data.get('name', author.name)
        author.save()
 
        return super(BookSerializer, self).update(instance, validated_data)

这个代码示例展示了如何在Django REST framework中定义一个序列化器,它包含了对复杂关系(比如一个模型与另一个模型之间的关系)的处理。在这个例子中,我们有一个Book模型和一个Author模型,它们通过一个ForeignKey关联。BookSerializer嵌套了AuthorSerializer,允许在创建和更新书籍时同时处理作者信息。代码中的createupdate方法展示了如何在保存书籍数据时也处理和保存作者数据。

2024-09-09

在C++中使用SQLite时产生中文乱码的原因通常是编码设置不正确。以下是解决方法:

  1. 确保数据库文件是以正确的编码格式创建的,通常使用UTF-8编码。
  2. 在连接数据库时,设置正确的编码。
  3. 在执行SQL查询前,设置会话编码为UTF-8。

示例代码:




#include <sqlite3.h>
 
int main() {
    sqlite3* db;
    sqlite3_open("database.db", &db);
 
    // 设置数据库编码为UTF-8
    sqlite3_exec(db, "PRAGMA encoding = 'UTF-8';", nullptr, nullptr, nullptr);
 
    // 设置会话编码为UTF-8
    sqlite3_exec(db, "PRAGMA foreign_keys = ON;", nullptr, nullptr, nullptr);
    sqlite3_exec(db, "PRAGMA journal_size_limit = 1048576;", nullptr, nullptr, nullptr);
    sqlite3_exec(db, "PRAGMA auto_vacuum = 1;", nullptr, nullptr, nullptr);
    sqlite3_exec(db, "PRAGMA cache_size = 2000;", nullptr, nullptr, nullptr);
 
    // 此时应该可以正确处理中文
    // ...
 
    sqlite3_close(db);
    return 0;
}

确保在创建数据库文件时,使用的是UTF-8编码。如果是从文本文件导入数据,确保文本文件也是UTF-8编码。如果是通过程序插入中文数据,确保在插入前将字符串转换为UTF-8编码。

如果上述方法仍然无法解决乱码问题,可能需要检查代码库中是否有其他地方可能导致编码不一致,或者检查是否有其他软件环节(如文本编辑器或终端)影响了编码。

2024-09-09



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
@Component
public class LoggingCommandLineRunner implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(LoggingCommandLineRunner.class);
 
    @Override
    public void run(String... args) {
        if (logger.isDebugEnabled()) {
            logger.debug("Debug message. Args: {}", (Object) args);
        }
        logger.info("Info message. Args: {}", (Object) args);
        logger.warn("Warn message. Args: {}", (Object) args);
        
        // 假设有一个方法用于检查参数是否包含某个关键字
        if (containsKeyword(args, "error")) {
            logger.error("Error message. Args: {}", (Object) args);
        }
    }
 
    private boolean containsKeyword(String[] args, String keyword) {
        // 这里是一个简化的检查逻辑,实际应用中需要更复杂的逻辑
        return Arrays.stream(args).anyMatch(arg -> arg.contains(keyword));
    }
}

这段代码演示了如何在Spring Boot应用程序中使用CommandLineRunner接口来执行启动时的操作。它还展示了如何使用Logger来记录不同级别的日志,并且使用isDebugEnabled来优化日志输出,避免不必要的字符串格式化计算。