当前位置: 首页 > article >正文

asp.net core webapi+efcore

简洁的restfull风格

目前c#提供了多种风格的web编程,因为微软有自己的前端,所以集成了很多内容,不过基于现在编程前后端分离的模式,webapi是合适的。

webapi

目前网络上有很多介绍,不反复说这个了。在建立控制器时,使用最多就是httpget和httppost,其它意义不大。

实操例子

准备库

 在visual studio建立webapi项目,默认即可。我使用的数据库是postgresql,下载对应的组件。

我用到的所有库:Microsoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.Tools,Microsoft.EntityFrameworkCore.Proxies,Npgsql.EntityFrameworkCore.PostgreSQL。其它都会项目创建自动有。

Microsoft.EntityFrameworkCore.Tools:介绍代码优先会用到。

Microsoft.EntityFrameworkCore.Proxies:介绍efcore优化用到。

Npgsql.EntityFrameworkCore.PostgreSQL:PostgreSQL数据库连接驱动

项目编程

(1)创建models文件夹,创建user类,Product类

  public class User{public int Id { get; set; }public string Name { get; set; }public string Email { get; set; }}
 public class Product{public int Id { get; set; }public string Name { get; set; }public decimal Price { get; set; }}

(2)创建PlatDbContext文件夹,创建AppDbContext类,数据库访问。

 public class AppDbContext : DbContext{public DbSet<Product> Products { get; set; }public DbSet<User> Users { get; set; }public AppDbContext(DbContextOptions<AppDbContext> options): base(options) { }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){//配置连接数据库字符串string connectionString = "Host=localhost;Username=postgres;Password=123456;Database=plat";//指定使用SqlServer数据库进行连接optionsBuilder.UseNpgsql(connectionString);//  optionsBuilder.UseLazyLoadingProxies();}protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Product>()//  .ToTable("Products").HasKey(p => p.Id);modelBuilder.Entity<Product>().Property(p => p.Name).HasMaxLength(100);}}

(3) 创建Repository文件夹,创建创存模式的接口和实现类

 public interface IRepository<T> where T : class{Task<IEnumerable<T>> GetAllAsync();Task<T> GetByIdAsync(int id);Task AddAsync(T entity);Task UpdateAsync(T entity);Task DeleteAsync(int id);Task<bool> SaveChangesAsync();//只读Task<IEnumerable<T>> GetAllAsNoTrackingAsync();Task<int> ExecuteSqlAsync(string sql);Task<IDbContextTransaction> GetTransactionAsync();}
   public class Repository<T> : IRepository<T> where T : class{private readonly AppDbContext _context;private readonly DbSet<T> _dbSet;public Repository(AppDbContext context){_context = context;_dbSet = _context.Set<T>();}public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();public async Task<T> GetByIdAsync(int id) => await _dbSet.FindAsync(id);public async Task AddAsync(T entity) {   await _dbSet.AddAsync(entity);await SaveChangesAsync();}public async Task UpdateAsync(T entity){_dbSet.Update(entity);await SaveChangesAsync();}public async Task DeleteAsync(int id){var entity = await _dbSet.FindAsync(id);if (entity != null){_dbSet.Remove(entity);await SaveChangesAsync();}}public async Task<IEnumerable<T>> GetAllAsNoTrackingAsync(){return  await _dbSet.AsNoTracking<T>().ToListAsync();}public async Task<int> ExecuteSqlAsync(string sql){return await _context.Database.ExecuteSqlRawAsync(sql);}public async Task<IDbContextTransaction> GetTransactionAsync(){return  await _context.Database.BeginTransactionAsync();}public async Task<bool> SaveChangesAsync() => await _context.SaveChangesAsync() > 0;}

(4)配置EF,在main方法中添加如下代码。

 builder.Services.AddDbContext<AppDbContext>(options =>options.UseNpgsql().LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }));
// builder.Services.AddDbContextPool<AppDbContext>(options => options.UseNpgsql(), poolSize: 80);//注意设置最大连接数,一旦超过默认配置的连接池最大数量,会回退到按需创建实例的行为builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

(5) 创建控制器UserController 

namespace WebPlat.Controllers
{[Route("api/[controller]")][ApiController]public class UserController : ControllerBase{public  UserController(IRepository<User> repository,ILogger<UserController> logger){this.repository = repository;this.logger = logger;}IRepository<User> repository;ILogger<UserController> logger;[HttpGet][Route("GetUser")]public IEnumerable<User> GetUser(){// 返回所有用户的逻辑return new List<User>{new User { Id = 1, Name = "Alice", Email = "alice@example.com" },new User { Id = 2, Name = "Bob", Email = "bob@example.com" }  // ... 其他用户数据  };}// GET api/Test/5  [HttpGet][Route("GetMyUser")]public User GetMyUser(int id){// 根据ID返回单个用户的逻辑  return new User { Id = 1, Name = "Alice", Email = "alice@example.com" };}[HttpPost("AddUser")]public bool AddUser([FromBody] User user){repository.AddAsync(user);return true;}}
}

(6) 服务层(简单的程序不需要)

如果你有很复杂的业务处理,那么还需要一个服务层,需要创建文件夹Services,在里面创建接口和实现类,也有把接口和类放到不同的文件夹(名称空间)。此时存储对象就需要放在业务层。控制器中使用服务接口。

 public interface IUserService{Task<User> GetUserByIdAsync(int id);Task<IEnumerable<User>> GetAllUsersAsync();Task<User> CreateUserAsync(User user);Task UpdateUserAsync(int id, User user);Task DeleteUserAsync(int id);}
 public class UserService : IUserService{private readonly IRepository<User>  repository; // 假设有一个 ApplicationDbContext 作为数据上下文public UserService(IRepository<User> repository){this.repository = repository;}public async Task<User> GetUserByIdAsync(int id){return await repository.GetByIdAsync(id);}public async Task<IEnumerable<User>> GetAllUsersAsync(){return await repository.GetAllAsync();}public async Task<User> CreateUserAsync(User user){await repository.AddAsync(user);return user;}public async Task UpdateUserAsync(int id, User user){await repository.UpdateAsync(id, user);}public async Task DeleteUserAsync(int id){await repository.DeleteAsync(id);}}

然后注入服务。

builder.Services.AddScoped(typeof(IUserService), typeof(UserService));

不好意思我偷了一下懒,不提倡。写了一个反射方法注入服务。

 public static void AddService(IServiceCollection services){// 获取当前程序集Assembly assembly = Assembly.GetExecutingAssembly();// 指定要搜索的命名空间string targetNamespace = "WebPlat.Services";// 获取所有类型Type[] types = assembly.GetTypes();// 过滤出特定命名空间下的所有类型var filteredTypes = types.Where(t => t.Namespace == targetNamespace).Where(t=>t.IsClass);foreach (var type in filteredTypes){Type[] interfaces= type.GetInterfaces();var interfacesTypes=  interfaces.Where(t => t.Namespace == targetNamespace);//可以不要,只是更加精确if (interfaces.Length > 0){foreach(Type interfacetype in interfacesTypes){ services.AddScoped(interfacetype, type);}}}}

在main方法注入服务就变成了调用该方法。

所以控制器中就变了一点,为了演示没有删除之前的。

 public  UserController(IRepository<User> repository,IUserService service, ILogger<UserController> logger)
 {
     this.repository = repository;
     this.logger = logger;
     this.userService = service;

 }
 IRepository<User> repository;
 ILogger<UserController> logger;

 IUserService userService;

添加了一个测试方法:

  [HttpPost("CreateUser")]public bool CreateUser([FromBody] UserVo user){// repository.AddAsync(user);string json= JsonUtil.ToJson(user);User user1= JsonUtil.ToObject<User>(json);user1= CopyUtil<UserVo, User>.Trans(user);userService.CreateUserAsync(user1);return true;}

有一点不一样?因为如果有复杂业务,一般来说前端的数据和最后数据库的数据可能不一样,就会专门有和前端交互的数据结构,就是dao层。有时候还有中间的实体层等。

既然有了2个不一样的类结构,是不是要赋值?c#浅拷贝,深拷贝了解一下。

这里一般主要的几种:c#自己的序列化(二进制,xml,json),手动写代码赋值,第三方等。

通用的就是序列化,目前主要是json,其它两种就别给自己找事了。

json序列化目前推荐使用第三方Newtonsoft.Json,不要用微软的,除非你自己闲的慌喜欢尝试。

因此又建立一个工具文件夹Utils,创建json序列化类。你也限制一下泛型参数。

  public class JsonUtil{public  static  string ToJson<T>(T obj){string json = JsonConvert.SerializeObject(obj);return json;}public static T ToObject<T>(string json){T obj = JsonConvert.DeserializeObject<T>(json);return obj;}}

这样就可以通过json复制对象,json复制对象类似反射,效率相对有点低,但是一般的程序应该是可以的,可以接受。

顺便提供一个优化的复制对象方法,表达式树。创建CopyUtil类。

 public static class CopyUtil<TIn, TOut>{private static readonly Func<TIn, TOut> cache = GetFunc();private static Func<TIn, TOut> GetFunc(){ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");List<MemberBinding> memberBindingList = new List<MemberBinding>();foreach (var item in typeof(TOut).GetProperties()){if (!item.CanWrite)continue;if(typeof(TIn).GetProperty(item.Name)==null){continue;}MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));MemberBinding memberBinding = Expression.Bind(item, property);memberBindingList.Add(memberBinding);}MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });return lambda.Compile();}public static TOut Trans(TIn tIn){return cache(tIn);}}

你可能不懂?不懂没有关系,反正就是复制属性。以前我研究过emit,写了一个通用方法,嗯效率很高,不过看起来有点反人类,这个表达式树比较通人性一点。两者原理一样,都是在内存中已经编译生成了。

EF的代码优先

下载完库,运行项目可以正常执行以后,准备生成数据库的表,这里我是介绍一下操作,不详细讲解ef的使用方法,ef的使用,在model上可以设置各种属性,我不详细介绍,太大。

(1)数据库   

   安装好postgresql数据库,客户端连接,建立好数据库:plat。

(2)生成数据库表

在visual studio的工具菜单中打开“程序包管理器控制台”,然后执行命令:Add-Migration InitialCreate 执行成功后,会在项目中生成一个文件夹和类,不用管。

然后再执行命令: Update-database 

以上成功则会在数据库生成表,如图:

补充

项目中创建一个全局筛选器,可以做很多有用的工作。例如项目的错误处理。

(1)项目中创建文件夹Filter,建立HttpExceptionFilterAttribute类

 public class HttpExceptionFilterAttribute : System.Web.Http.Filters.ExceptionFilterAttribute{public override void OnException(HttpActionExecutedContext actionExecutedContext){if (actionExecutedContext.Exception is Exception ex){actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message);}}}

EF优化

EFcore速度一直是比较有争议,不过目前最新版本感觉已经基本可以了,这里把最主要的优化简单介绍。

(1)延迟加载

启用延迟配置,在AppDbContext的配置方法中添加配置语句。该功能的详细意义需要自己详细了解。

optionsBuilder.UseLazyLoadingProxies();

(2)上下文池化

需要再配置上下文时使用池化。用该语句替换一般的配置。

builder.Services.AddDbContextPool<AppDbContext>(options => options.UseNpgsql(), poolSize: 80);

(3)查询只读,启用AsNoTracking.

就是只查询不跟踪,在项目中就是仓存模式的GetAllAsNoTrackingAsync方法。

目前主要的优化就是上面几种,当然对于程序来说,你可以根据业务优化,在查询中启用缓存的方式等。还有一些其它库,例如:Z.EntityFramework.Plus.EFCore。该第三方库可以研究。

这里说一下批量添加,批量添加目前很多数据库支持批量sql语句,例如:

insert  into(ID,name)value(1,'ton') value(2,'j') ;这样的语句,可以充分使。

还有很多数据库提供了bulkcopy,支持ado.net方式,所以有了efcore后,原始基础的ado.net还是精华,需要了解。

最后的总结

  这里只是webapi,其实不喜欢代码优先或者数据库优先这种模式。个人觉得数据库还是数据库,代码还是代码,传统的数据库设计创建,程序开发就是程序,这里model会少很多属性,数据库表会很准确。目前这样的方式出现,很多小企业都是程序员自己弄数据库,开发程序,看起来会少很多工作,其实不好。还是分离好点,还有助于检测。

  另外你是winfrom和WPF等桌面系统,也建议可以使用后台服务webapi。目前已经不需要用IIS部署了。这样显示端是web或桌面都无所谓。至于性能问题,可以在某些功能下使用TCP或者grpc。

桌面系统访问建议使用httpclient库。 

相关文章:

asp.net core webapi+efcore

简洁的restfull风格 目前c#提供了多种风格的web编程&#xff0c;因为微软有自己的前端&#xff0c;所以集成了很多内容&#xff0c;不过基于现在编程前后端分离的模式&#xff0c;webapi是合适的。 webapi 目前网络上有很多介绍&#xff0c;不反复说这个了。在建立控制器时&…...

前端渲染pdf文件解决方案-pdf.js

目录 一、前言 二、简介 1、pdf.js介绍 2、插件版本参数 三、通过viewer.html实现预览&#xff08;推荐&#xff09; 1、介绍 2、部署 【1】下载插件包 【2】客户端方式 【3】服务端方式&#xff08;待验证&#xff09; 3、使用方法 【1】预览PDF文件 【2】外部搜索…...

vue3 + element-plus中el-drawer抽屉滚动条回到顶部

el-drawer抽屉滚动条回到顶部 <script setup lang"ts" name"PerformanceLogQuery"> import { ref, nextTick } from "vue"; ...... // 详情 import { performanceLogQueryByIdService } from "/api/performanceLog"; const onD…...

从边缘到云端,如何通过时序数据库 TDengine 实现数据的全局洞

在当今数字化转型加速的背景下&#xff0c;海量的数据生成和实时处理需求已成为企业面临的关键挑战。无论是物联网设备、工业自动化系统&#xff0c;还是智能城市的各类传感器&#xff0c;数据的采集、传输与分析效率&#xff0c;直接影响企业的决策与运营。为此&#xff0c;TD…...

2025.04.23【Treemap】树状图数据可视化指南

Multi-level treemap How to build a treemap with group and subgroups. Customization Customize treemap labels, borders, color palette and more 文章目录 Multi-level treemapCustomization Treemap 数据可视化指南Treemap 的基本概念为什么使用 TreemapTreemap 的应用…...

蓝桥杯 15.小数第n位

小数第n位 原题目链接 题目描述 我们知道&#xff0c;整数做除法时&#xff0c;有时会得到有限小数&#xff0c;有时会得到无限循环小数。 如果我们把有限小数的末尾加上无限多个 0&#xff0c;它们就具有了统一的形式。 本题的任务是&#xff1a;在上述约定下&#xff0c…...

用高斯溅射技术跨越机器人模拟与现实的鸿沟:SplatSim 框架解析

在机器人领域&#xff0c;让机器人在现实世界中精准执行任务是大家一直追求的目标。可模拟环境和现实世界之间存在着不小的差距&#xff0c;特别是基于 RGB 图像的操作策略&#xff0c;从模拟转移到现实时总是状况百出。 今天咱们就来聊聊 SplatSim 框架&#xff0c;看看它是怎…...

Transformer 架构 - 解码器 (Transformer Architecture - Decoder)

欢迎回到我们的 Transformer 系列教程!在上一篇中,我们详细探讨了 Transformer 的编码器,它负责将输入的源序列(比如源语言句子)转换为一系列包含丰富上下文信息的向量表示。 现在,我们将把目光投向 Transformer 的另一半——解码器 (Decoder)。解码器负责接收编码器的输…...

使用Intel Advisor工具分析程序

使用Intel Advisor工具分析程序 Intel Advisor是一款性能分析工具&#xff0c;主要用于识别代码中的向量化机会、线程化和内存访问模式等问题。以下是使用Intel Advisor分析程序的基本步骤&#xff1a; 安装与准备 从Intel官网下载并安装Intel Advisor&#xff08;通常作为I…...

AI大模型学习十一:‌尝鲜ubuntu 25.04 桌面版私有化sealos cloud + devbox+minio,实战运行成功

一、说明 用了ubuntu 25.04&#xff0c;内核为GNU/Linux 6.14.0-15-generic x86_64&#xff0c;升级了部分image&#xff0c;过程曲折啊 sealos 能干啥 对集群生命周期进行管理&#xff0c;一键安装高可用 Kubernetes 集群&#xff0c;增删节点清理集群自恢复等 通过 sealos…...

如何在 Python 项目中引入 Rust 函数

目录 1. 初始化 Python 项目2. 添加 Rust 开发工具3. 初始化 Rust 项目4. 开发模式构建5. 验证模块是否成功安装6. 测试 Rust 函数总结 (封面pid: 129416070) Python 是一门非常流行的编程语言&#xff0c;具有易于使用和开发的特点。然而&#xff0c;随着项目需求的增长和性能…...

聊聊SpringAI流式输出的底层实现?

在 Spring AI 中&#xff0c;流式输出&#xff08;Streaming Output&#xff09;是一种逐步返回 AI 模型生成结果的技术&#xff0c;允许服务器将响应内容分批次实时传输给客户端&#xff0c;而不是等待全部内容生成完毕后再一次性返回。 这种机制能显著提升用户体验&#xff…...

MySQL 8 自动安装脚本(CentOS-7 系统)

文章目录 一、MySQL 8 自动安装脚本脚本说明&#x1f4cc; 使用脚本前提条件1. 操作系统2. 用户权限3. 网络要求 &#x1f4cc; 脚本的主要功能1. 环境检查2. MySQL 自动安装3. 自动配置 MySQL4. 防火墙配置5. 验证与输出 &#x1f4cc; 适用场景 二、执行sh脚本1. 给予脚本执行…...

在Notepad++中使用NppAtyle插件格式化代码

参考链接&#xff1a;Artistic Style 使用教程&#xff08;中文版&#xff09; 1.下载NppAStyle插件&#xff08;根据版本&#xff0c;选择32位或者64位&#xff09; https://github.com/ywx/NppAStyle/releases 2.菜单栏中选择&#xff1a;插件->打开插件文件夹 创建文件夹…...

【k8s系列7-更新中】kubeadm搭建Kubernetes高可用集群-三主两从

主机准备 结合前面的章节,这里需要5台机器,可以先创建一台虚拟机作为基础虚拟机。优先把5台机器的公共部分优先在一台机器上配置好 1、配置好静态IP地址 2、主机名宇IP地址解析 [root@localhost ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost…...

拼多多面经,暑期实习Java一面

项目中的设计模式 mysql连接过程&#xff0c;索引&#xff0c;分库分表场景&#xff0c;路由策略 redis使用场景&#xff0c;分片集群怎么搭建与路由&#xff0c;数据一致性 分布式锁怎么用的&#xff0c;具体使用参数 线程池怎么用的&#xff0c;过程 sql having 分布式事务 如…...

FramePack:让视频生成更高效、更实用

想要掌握如何将大模型的力量发挥到极致吗&#xff1f;叶梓老师带您深入了解 Llama Factory —— 一款革命性的大模型微调工具&#xff08;限时免费&#xff09;。 1小时实战课程&#xff0c;您将学习到如何轻松上手并有效利用 Llama Factory 来微调您的模型&#xff0c;以发挥其…...

ctfshow web8

前言 学习内容&#xff1a;简单的盲注脚本的书写 web8 这个题目题目手动注入很麻烦 主要是他过滤了 union 空格和 过滤了union的解决方法 1、使用盲注(报错注入和盲注) 2、使用时间盲注 3、堆叠注入 盲注脚本的书写 首先他是有注入点的 然后熟悉requests包的使用 …...

gem5-gpu教程03 当前的gem5-gpu软件架构(因为涉及太多专业名词所以用英语表达)

Current gem5-gpu Software Architecture 这是当前gem5-gpu软件架构的示意图。 Ruby是在gem5-gpu上下文中用于处理CPU和GPU之间内存访问的高度可配置的内存系统 CudaCore (src/gpu/gpgpu-sim/cuda_core.*, src/gpu/gpgpu-sim/CudaCore.py) Wrapper for GPGPU-Sim shader_cor…...

TDengine 查询引擎设计

简介 TDengine 作为一个高性能的时序大数据平台&#xff0c;其查询与计算功能是核心组件之一。该平台提供了丰富的查询处理功能&#xff0c;不仅包括常规的聚合查询&#xff0c;还涵盖了时序数据的窗口查询、统计聚合等高级功能。这些查询计算任务需要 taosc、vnode、qnode 和…...

AOSP Android14 Launcher3——点击桌面图标启动应用动画流程

在Launcher3中&#xff0c;点击桌面应用图标时&#xff0c;会有一个从 图标位置起始到全屏的动画过程&#xff0c;使得应用的打开过程不是生硬的启动过程。 这个动画具体是怎么实现的呢&#xff1f;本文对这个过程进行一个梳理 在Launcher中&#xff0c;动画大体上可以分为两类…...

windows端远程控制ubuntu运行脚本程序并转发ubuntu端脚本输出的网页

背景 对于一些只能在ubuntu上运行的脚本&#xff0c;并且这个脚本会在ubuntu上通过网页展示运行结果。我们希望可以使用windows远程操控ubuntu&#xff0c;在windows上查看网页内容。 方法 start cmd.exe /k "sshpass -p passwd ssh namexxx.xxx.xxx.xxx "cd /hom…...

【官方正版,永久免费】Adobe Camera Raw 17.2 win/Mac版本 配合Adobe22-25系列软

Adobe Camera Raw 2025 年 2 月版&#xff08;版本 17.2&#xff09;。目前为止最新版新版已经更新2个月了&#xff0c;我看论坛之前分享的还是2024版&#xff0c;遂将新版分享给各位。 Adobe Camera Raw&#xff0c;支持Photoshop&#xff0c;lightroom等Adobe系列软件&#…...

如何使用flatten函数在Terraform 中迭代嵌套map

简介 flatten 接受一个列表&#xff0c;并用列表内容的扁平序列替换列表中的任何元素。 > flatten([["a", "b"], [], ["c"]]) ["a", "b", "c"] > flatten([[["a", "b"], []], [&quo…...

原生 HTML 的`title` 属性修改触发事件为鼠标移入移出显示

HTML中title属性的适用范围 在 HTML 中,title 属性是全局属性(Global Attribute),这意味着它可以被应用到所有 HTML 标签上。无论是块级元素(如 <div>)、行内元素(如 <span>),还是表单元素(如 <input>),都可以添加 title 属性。 常见使用 title…...

【论文精读】Reformer:高效Transformer如何突破长序列处理瓶颈?

目录 一、引言&#xff1a;当Transformer遇到长序列瓶颈二、核心技术解析&#xff1a;从暴力计算到智能优化1. 局部敏感哈希注意力&#xff08;LSH Attention&#xff09;&#xff1a;用“聚类筛选”替代“全量计算”关键步骤&#xff1a;数学优化&#xff1a; 2. 可逆残差网络…...

jmeter中监控服务器ServerAgent

插件下载&#xff1a; 将ServerAgent上传至需要监控的服务器&#xff0c;mac/liunx启动startAgent.sh&#xff08;启动命令&#xff1a;./startAgent.sh&#xff09; 在jmeter中添加permon监控组件 配置需要监控的服务器IP地址&#xff0c;添加需要监控的资源 注意&#xf…...

网络结构及安全科普

文章目录 终端联网网络硬件基础网络协议示例&#xff1a;用户访问网页 OSI七层模型网络攻击&#xff08;Hack&#xff09;网络攻击的主要类别&#xff08;一&#xff09;按攻击目标分类&#xff08;二&#xff09;按攻击技术分类 网络安全防御 典型攻击案例相关名词介绍网络连接…...

JVM虚拟机-JVM调优、内存泄漏排查、CPU飙高排查

一、JVM调优的参数在哪里设置 项目开发过程中有以下两种部署项目的方式&#xff1a; 项目部署在tomcat中&#xff0c;是一个war包&#xff1b;项目部署在SpringBoot中&#xff0c;是一个jar包。 (1)war包 catalina文件在Linux系统下的tomcat是以sh结尾&#xff0c;在windows系…...

安全复健|windows常见取证工具

写在前面&#xff1a; 此博客仅用于记录个人学习内容&#xff0c;学识浅薄&#xff0c;若有错误观点欢迎评论区指出。欢迎各位前来交流。&#xff08;部分材料来源网络&#xff0c;若有侵权&#xff0c;立即删除&#xff09; 取证 01系统运行数据 使用工具&#xff1a;Live-F…...