Abp Vnext Vue3 的版本实现

Abp Vnext Pro 的 Vue3 实现版本 开箱即用的中后台前端/设计解决方案

84709a456d2a8d388df7d7f5664ff544.png61057960e0e2f6bd147a78c55a132391.png
92415f4184286ba274f7da820a5bf080.png1cb61a975421f348aa87afe8efebcf25.png
f186d612bb04a5796d28b3012b945f0e.png62a4a4424d689fd9f19e2b5f4dd50f25.png

开始

  • Github地址

  • 文档地址

  • 演示地址

系统功能

  • [x] 用户管理

  • [x] 角色管理

  • [x] 审计日志

  • [x] 后台任务

  • [x] 集成事件

  • [x] IdentityServer4

    • [x] 客户端管理

    • [x] Api 资源管理

    • [x] ApiScope 管理

    • [x] Identity 资源管理

  • [x] SinglaR 消息通知

  • [x] 多语言

  • [x] FreeSql

  • [x] 数据字典(UI 暂时没有)

  • [x] 容器化部署

  • [x] 单元测试

  • [x] ES 日志

  • [x] Setting 管理

  • [x] 多租户

  • [ ] 组织机构

项目结构

后端

.
├── Directory.Build.props nuget 版本控制
├── frameworks # 公共模块
│       ├── CAP # dotnetcore.cap
│       └── Extensions # 自定义扩展
├── gateways # 网关
├── modules # 模块
│       ├── DataDictionaryManagement # 数据字典
│       └── NotificationManagement # 通知服务
├── services # 公共静态资源目录
│       ├── host # 启动模块
│           ├── CompanyName.ProjectName.HttpApi.Host # admin ui host
│           └── CompanyName.ProjectName.IdentityServer # IdentityServer host
│       ├── src  # 源码
│           └── CompanyName.ProjectName.DbMigrator # 迁移控制台程序
│       └── test # 单元测试

前端

.
├── _nginx # docker 打包
├── build # 打包脚本相关
│   ├── config # 配置文件
│   ├── generate # 生成器
│   ├── script # 脚本
│   └── vite # vite配置
├── mock # mock文件夹
├── public # 公共静态资源目录
├── src # 主目录
│   ├── api # 接口文件
│   ├── assets # 资源文件
│   │   ├── icons # icon sprite 图标文件夹
│   │   ├── images # 项目存放图片的文件夹
│   │   └── svg # 项目存放svg图片的文件夹
│   ├── components # 公共组件
│   ├── design # 样式文件
│   ├── directives # 指令
│   ├── enums # 枚举/常量
│   ├── hooks # hook
│   │   ├── component # 组件相关hook
│   │   ├── core # 基础hook
│   │   ├── event # 事件相关hook
│   │   ├── setting # 配置相关hook
│   │   └── web # web相关hook
│   ├── layouts # 布局文件
│   │   ├── default # 默认布局
│   │   ├── iframe # iframe布局
│   │   └── page # 页面布局
│   ├── locales # 多语言
│   ├── logics # 逻辑
│   ├── main.ts # 主入口
│   ├── router # 路由配置
│   ├── services # Nswag生成的代理
│   │   ├── ServiceProxies.ts # Nswag生成的代理
│   │   ├── ServiceProxyBase.ts # Nswag生成的代理拦截器
│   ├── settings # 项目配置
│   │   ├── componentSetting.ts # 组件配置
│   │   ├── designSetting.ts # 样式配置
│   │   ├── encryptionSetting.ts # 加密配置
│   │   ├── localeSetting.ts # 多语言配置
│   │   ├── projectSetting.ts # 项目配置
│   │   └── siteSetting.ts # 站点配置
│   ├── store # 数据仓库
│   ├── utils # 工具类
│   └── views # 页面
├── test # 测试
│   └── server # 测试用到的服务
│       ├── api # 测试服务器
│       ├── upload # 测试上传服务器
│       └── websocket # 测试ws服务器
├── types # 类型文件
├── vite.config.ts # vite配置文件
└── windi.config.ts # windcss配置文件

运行项目前提

  • Mysql

    docker run --name mymysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1q2w3E* -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  • Redis

    docker run --name myredis -p 6379:6379 -d redis:latest redis-server
  • RabbitMq 非必须

  • appsetting.development.json-> CAP:Enabled 设置为 false

    docker run -d --name myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management
  • ELK 非必须

  • appsetting.development.json-> LogToElasticSearch:Enabled 设置为 false

  • 安装 Node.js, Npm Or Yarn

获取项目

  • 直接 clone 项目

git clone https://github.com/WangJunZzz/abp-vnext-pro.git

OR

  • 下载代码生成器

git clone https://github.com/WangJunZzz/abp-vnext-pro-gui.git
  • 下载代码生成生成器之后,输入自己想要的项目名称生成代码即可

启动

  • 修改 HttpApi.Host-> appsettings.development.json 的数据库连接字符串,Redis, RabbitMq,Es 地址即可(如果没有 es 也可以运行,只是前端 es 日志页面无法使用而已,不影响后端项目启动)

  • 修改 IdentityServer-> appsettings.development.json 数据库连接字符串

  • 修改 DbMigrator-> appsettings.json 数据库连接字符串

  • 运行 DbMigrator 生成数据库

  • 启动 HttpApi.Host 和 IdentityServer

  • 前端 yarn 之后,执行 npm run dev 启动

配置说明

  • HttpApi.Host-> appsettings.development.json

{// Serilog 日志配置,生成环境修改日志级别"Serilog": {"MinimumLevel": {"Default": "Information","Override": {"Microsoft": "Information","Volo.Abp": "Information","Hangfire": "Information","DotNetCore.CAP": "Information","Serilog.AspNetCore": "Information"}}},// 跨域设置"App": {"CorsOrigins": "https://*.ProjectName.com,http://localhost:4200,http://localhost:3100"},// 数据库连接字符串,修改为你本地的mysql地址"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"},// Redis缓存"Cache": {"Redis": {"ConnectionString": "localhost","Password": "mypassword","DatabaseId": 0}},// Jwt配置"Jwt": {"Audience": "CompanyNameProjectName",//客户端标识"SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=","Issuer": "CompanyNameProjectName",//签发者"ExpirationTime": 24//过期时间 hour},// 使用了Dotnetcore.cap的rabbitmq,false的情况基于内存"Cap": {"Enabled": "false","RabbitMq": {"HostName": "localhost","UserName": "admin","Password": "admin"}},// es日志地址配置"LogToElasticSearch": {"Enabled": "true","ElasticSearch": {"Url": "http://es.cn","IndexFormat": "companyname.projectname.development","UserName": "elastic","Password": "aVVhjQ95RP7nbwNy","DashboardIndex": "companyname.projectname"}},// identityserver地址"HttpClient": {"Sts": {"Url": "http://localhost:44354"}},// Consul 服务发现和治理"Consul": {"Enabled": false,"Host": "http://localhost:8500","Service": "Project-Service"}
}
  • IdentityServer-> appsettings.development.json

{"App": {"SelfUrl": "https://localhost:44354","ClientUrl": "http://localhost:4200","CorsOrigins": "https://*.ProjectName.com,http://localhost:4200,https://localhost:44307,https://localhost:44315","RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307"},// mysql连接字符串"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"},// Redis"Redis": {"Configuration": "localhost,password=mypassword"}
}
  • DbMigrator-> appsettings.json

// 迁移数据库"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"}

前端

  • 前端采用 TypeScript,所有的类型动态生成 NSwag

  • 后端 api 统一使用 Post

  • 定义 api 格式

// 一定要打Tags,因为前端会根据这个生成代理类
// 建议参数都封装为一个Input
[SwaggerOperation(summary: "登录", Tags = new[] {"Account"})]
public Task<LoginOutput> LoginAsync(LoginInput input)
{return _loginAppService.LoginAsync(input);
}
  • 在前端目录下配置代理的地址

    • nswag->nswag.json

"documentGenerator": {"fromDocument": {"url": "http://localhost:44315/swagger/v1/swagger.json", // 代理地址,只有生成的时候用,不区分环境}}
  • 如果接口参数或者返回值有改变,需要重新生成代理,执行:

npm run nswag
  • 前端多环境,.env.development 和.env.production

    • 接口地址配置 VITE_API_URL

    • IdentityServer 地址配置 VITE_AUTH_URL

  • 权限配置

  • 菜单权限

    • src/router/routes

      policy 字段匹配后端的权限名称

    • 按钮权限

      v-auth="'AbpIdentity.Users.Delete'"

健康检查

模块

用户管理

  • 提供原始登录和第三方登录(IdentityServer4),默认用户名密码:admin 1q2w3*

角色管理

  • 权限定义(Application.Contracts 层)

  • Abp 会自动扫描继承 PermissionDefinitionProvider

  • 文档 Abp 官方

  • 在 Http.Api 的 Controller 打上 Authorize

设置管理

  • 集成Abp.SettingUi

消息通知

  • 消息类型,发送给指定人和广播消息

  • 发送消息到前端,通过集成事件和 RabbitMq

  • 注入 NotificationManager 发送消息,

/// <summary>
/// 发送普通文本消息
/// </summary>
/// <returns></returns>
/// <exception cref="NotificationManagementDomainException"></exception>
public async Task<Notification> SendCommonTextAsync(string title, string content, List<Guid> receiveIds)
{if (receiveIds is {Count: 0}){throw new NotificationManagementDomainException("消息接收人不能为空");var senderId = Guid.Empty;if (_currentUser?.Id != null){senderId = _currentUser.Id.Value;var entity = new Notification(GuidGenerator.Create(), title, content, MessageType.Text, senderId);foreach (var item in receiveIds){entity.AddNotificationSubscription(GuidGenerator.Create(), item);var notificationEto = ObjectMapper.Map<Notification, NotificationEto>(entity);// 发送集成事件entity.AddCreatedNotificationDistributedEvent(new CreatedNotificationDistributedEvent(notificationEto));return entity = await _notificationRepository.InsertAsync(entity);
}
  • Handler 当前事件:NotificationCreatedDistributedEventHandler

/// <summary>
/// 发送消息
/// </summary>
public async Task SendMessageAsync(string title, string content, MessageType messageType, List<string> users)
{switch (messageType){case MessageType.Text:await SendMessageToClientByUserIdAsync(new SendNotificationDto(title, content, messageType), users);break;case MessageType.BroadCast:await SendMessageToAllClientAsync(new SendNotificationDto(title, content, messageType));break;default:throw new UserFriendlyException("未知的消息类型");}
}
  • 前端接受 SignalR 消息

// src/hooks/web/useSignalR.js
import * as signalR from "@microsoft/signalr";
import { useMessage } from "/@/hooks/web/useMessage";
import { useUserStoreWithOut } from "/@/store/modules/user";
export function useSignalR() {/*** 开始连接SignalR*/function startConnect(): void {let connection = connectionsignalR();//接收普通文本消息connection.on("ReceiveTextMessageAsync", ReceiveTextMessageHandlerAsync);//接收广播消息connection.on("ReceiveBroadCastMessageAsync", ReceiveBroadCastMessageHandlerAsync);//开始连接connection.start();}/*** 连接signalr*/function connectionsignalR(): signalR.HubConnection {const userStore = useUserStoreWithOut();const token = userStore.getToken;const url = (import.meta.env.VITE_WEBSOCKE_URL as string) + "/ws/signalr/notification";const connection = new signalR.HubConnectionBuilder().withUrl(url, {accessTokenFactory: () => token,skipNegotiation: true,transport: signalR.HttpTransportType.WebSockets,}).withAutomaticReconnect({nextRetryDelayInMilliseconds: (retryContext) => {//重连规则:重连次数<300:间隔1s;重试次数<3000:间隔3s;重试次数>3000:间隔30slet count = retryContext.previousRetryCount / 300;if (count < 1) {//重试次数<300,间隔1sreturn 1000;} else if (count < 10) {//重试次数>300:间隔5sreturn 1000 * 5;} //重试次数>3000:间隔30selse {return 1000 * 30;}},}).configureLogging(signalR.LogLevel.Debug).build();return connection;}/*** 接收文本消息* @param message 消息体*/function ReceiveTextMessageHandlerAsync(message: any) {console.log(message);const { notification } = useMessage();notification.open({message: message.title,description: message.content,});}/*** 接收广播消息* @param message 消息体*/function ReceiveBroadCastMessageHandlerAsync(message: any) {const { notification } = useMessage();notification.open({message: message.title,description: message.content,});}return { startConnect };
}

审计日志

  • 参考 Abp 官方文档即可

ES 日志

  • 在 appsetting.development.json 设置是否开启

"LogToElasticSearch": {"Enabled": "false", // 如果为fasel,日志也会写入到本地,安装ELK,参考上面的docker-compose"ElasticSearch": {"Url": "http://es.cn","IndexFormat": "companyname.projectname.development","UserName": "elastic","Password": "aVVhjQ95RP7nbwNy","DashboardIndex": "companyname.projectname"}},

后台任务

  • 定时任务

public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
{context.CreateRecurringJob();base.OnPostApplicationInitialization(context);
}
  • 延迟任务: 官方文档

集成事件

  • 集成 dotnetcore.CAP

  • 在 appsetting.development.json 设置是否开启

"Cap": {"Enabled": "false", //如果为false 默认使用内存级别的队列,否则请安装rabbitmq"RabbitMq": {"HostName": "localhost","UserName": "admin","Password": "admin"}},
private void ConfigurationCap(ServiceConfigurationContext context)
{var configuration = context.Services.GetConfiguration();var enabled = configuration.GetValue<bool>("Cap:Enabled", false);if (enabled){context.AddAbpCap(capOptions =>{capOptions.UseEntityFramework<ProjectNameDbContext>();capOptions.UseRabbitMQ(option =>{option.HostName = configuration.GetValue<string>("Cap:RabbitMq:HostName");option.UserName = configuration.GetValue<string>("Cap:RabbitMq:UserName");option.Password = configuration.GetValue<string>("Cap:RabbitMq:Password");});var hostingEnvironment = context.Services.GetHostingEnvironment();bool auth = !hostingEnvironment.IsDevelopment();capOptions.UseDashboard(options => { options.UseAuth = auth; });});}else{context.AddAbpCap(capOptions =>{capOptions.UseInMemoryStorage();capOptions.UseInMemoryMessageQueue();var hostingEnvironment = context.Services.GetHostingEnvironment();bool auth = !hostingEnvironment.IsDevelopment();capOptions.UseDashboard(options => { options.UseAuth = auth; });});}
}
  • 发布事件

    • 可参考通知模块

// 发送集成事件
entity.AddCreatedNotificationDistributedEvent(new CreatedNotificationDistributedEvent(notificationEto));
  • 订阅事件

    • 可参考通知模块

/// <summary>
/// 创建消息事件处理
/// </summary>
public classCreatedNotificationDistributedEventHandler : IDistributedEventHandler<CreatedNotificationDistributedEvent>,ITransientDependency
{private readonly INotificationAppService _hubAppService;public CreatedNotificationDistributedEventHandler(INotificationAppService hubAppService){_hubAppService = hubAppService;}public Task HandleEventAsync(CreatedNotificationDistributedEvent eventData){return _hubAppService.SendMessageAsync(eventData.NotificationEto.Title,eventData.NotificationEto.Content,eventData.NotificationEto.MessageType,eventData.NotificationEto.NotificationSubscriptions.Select(e => e.ReceiveId.ToString()).ToList());}
}

身份认证中心

  • IdentityServer4

  • 可重写登录界面 UI

租户管理

  • 提供租户登录和 IdentityServer4 租户登录方式

Ocelot 网关(可选)

  • 集成 Ocelot 和 Consul

部署

Docker 方式

HttpApi.Host

  • 发布 HttpApi.Host 到和 Dockerfile 同级目录

    -- publish
    -- Dockerfile
  • Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:5.0# 创建目录
RUN mkdir /appCOPY publish /app# 设置工作目录
WORKDIR /app# 暴露80端口
EXPOSE 80# 设置环境变量
ENV ASPNETCORE_ENVIRONMENT=ProductionENTRYPOINT ["dotnet", "CompanyName.ProjectName.HttpApi.Host.dll"]
  • 生成 Docker 镜像

docker build -t abp-vnext-pro-admin .
  • 运行容器

docker run -itd --name abp-vnext-pro-admin -p 8011:80 abp-vnext-pro-admin

IdentityServer.Host

  • 步骤同上

前端

  • 打包

npm run build
  • Dockerfile

FROM nginx:1.17.3-alpine as base
EXPOSE 80
COPY /_nginx/nginx.conf /etc/nginx/nginx.conf
COPY /_nginx/env.js /etc/nginx/env.js
COPY /_nginx/default.conf /etc/nginx/conf.d/default.conf
COPY /dist/ /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
  • 生成 Docker 镜像

docker build -t abp-vnext-pro-ui .
  • 运行容器

docker run -itd --name abp-vnext-pro-ui -p 8012:80 abp-vnext-pro-ui

常见问题

VS 编译项目字符串超过 256 个字符

  • 把项目拷贝到磁盘根目录 OR 使用 Rider 开发

Hangfire 和 Cap 界面加载不出来

  • 这 2 个界面开启了权限认证,由于前端路由的异步加载,导致路由在渲染的时候 access_token 没有加载出来,Ctrl+F5 刷新即可

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

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

相关文章

struts2配置中Action的name 和package的name和namespace作用

2019独角兽企业重金招聘Python工程师标准>>> struts2配置中Action的name 和package的name和namespace是什么作用 在struts2 struts.xml 配置中 <package name"ajax" extends"json-default" namespace"/json"> <action na…

【译】Tablix指南----通向报表服务的阶梯系列(四)

“Ah ha,发现你了&#xff01;”你的经理突然从桌子后面的假花旁冒出来&#xff0c;发出胜利的叫声。你沮丧地转过头看着经理。 “我已经创建了一个基本报表&#xff0c;并抓取了一些数据&#xff0c;我正打算-”你在他打断你之前快速的解释着。 “赶紧做完就好&#xff0c;大B…

熊吃人该不该杀?这头3米高的大熊吃了7个人,还在洞里藏了很多女人用的东西.........

全世界只有3.14 % 的人关注了爆炸吧知识10月18日&#xff0c;有网友爆料称“上海野生动物园猛兽区一饲养员遭群熊撕扯”。随后&#xff0c;上海野生动物园证实&#xff0c;该饲养员目前已身亡。据悉&#xff0c;他才27岁&#xff0c;还没成家。从网上发布的现场视频来看&#x…

还在纠结Dapper或者EF Core?不妨试试“混合ORM”--RepoDb

在.NET世界中&#xff0c;提到ORM&#xff0c;最常用的是下面2个&#xff1a;Dapper&#xff0c;通过提供IDbConnection的扩展方法来查询数据库。它的优点在于可以轻松地执行参数化查询&#xff0c;将结果转化为对象&#xff1b;非常高效&#xff0c;在速度方面拥有micro-ORM之…

使用 PHP 7 给 Web 应用加速

PHP 20周年了&#xff01;&#xff1f;&#xff1f; PHP 首发通告&#xff0c;1995年6月8日 发布于 COMP.INFOSYSTEMS.WWW.AUTHORING.CGI 主题&#xff1a;正式宣布&#xff1a;个人主页工具&#xff08;Personal Home Page Tools&#xff09;正式宣布个人主页工具&#xff08;…

mvc教程

第一章 mvc概述 入门 需要安装 Visual Studio 2008 或 Visual Web Developer 2008 Express 来构建 ASP.NET MVC 应用程序。还需要下载 ASP.NET MVC Framework。 如果没有 Visual Studio 2008&#xff0c;可以从下面的网址下载 90 天试用版&#xff1a; http://msdn.microsoft.c…

windows下面怎么github ssh 公钥,然后克隆项目

windos下面怎么github ssh 公钥,然后克隆项目 第一步、 1. 安装git,从程序目录打开 "Git Bash" 2. 键入命令:ssh-keygen -t rsa -C "email@email.com" 我的邮箱是chen_yu@kuyu.com , "email@email.com"是github账号 3. 提醒你输入key的…

连破四次吉尼斯世界纪录!厨师界再出神人,用一公斤拉面缔造了一代传奇,背后却是简单的原理……

全世界只有3.14 % 的人关注了爆炸吧知识超模君最喜欢的就是公司楼下海底捞火锅面。不仅是面确实好吃&#xff0c;更重要的是因为海底捞拉面小哥的技艺十分精湛&#xff01;提臀、收腹&#xff0c;只见拉面小哥化身蹦迪达人&#xff0c;在旋转跳跃中舞动拉面。一顿猛如虎的操作之…

微软推出 .NET 官方社区论坛

James 近日发布博客&#xff08;https://devblogs.microsoft.com/dotnet/introducing-the-net-tech-community-forums/&#xff09;&#xff0c;推出 .NET 官方社区论坛&#xff0c;为开发者提供一个官方的交流平台&#xff0c;小道消息&#xff0c;微软即将上线中文版的.NET网…

android sdk中添加自定义api,android SDK中添加自定义api【转】

本文的思路&#xff1a;android APP调用自定义java API,自定义java API调用自定义jni接口1&#xff1a;在android源码目录framework/base下面创建add目录add目录里面的结构如下&#xff1a;里面的Nadd.java android_jnitest_Nadd.cpp 和Android.mk内容如下&#xff1a;Nadd.jav…

远程控制利器TeamViewer使用教程(图)

TeamViewer是什么&#xff1f; 他是一款免费的可以穿透内网的远程控制软件&#xff0c;可以实现桌面共享&#xff0c;文件传送等功能&#xff0c;简单一点说就是和QQ远程协助一样&#xff0c;但是比QQ的远程协助功能更为强大。 TeamViewer与木马有什么区别&#xff1f; 说到远程…

千万别让男朋友穿你的短裙......

1 工作太辛苦&#xff0c;我要走了...▼2 应该很有效&#xff1f;▼3 疑车有据&#xff01;&#xff01;▼4 这是一部色魔功法...▼5 穿上女友的衣服居然毫无违和感▼视频来源见水印&#xff0c;侵删6 整挺好&#xff01;▼7 这只有熊猫可以驾驭得了......▼你点的每个赞&…

迎接.NET 6,《dotnet+Linux中文手册》完整PDF开放下载!

昨晚已经正式发布了.NET6,具体参见&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-net-6/。这是.NET团队和社区努力一年的成果&#xff0c;C# 10 和 F# 6 有很大的语言改进&#xff0c;使代码更简单、更好。性能有了巨大的提升&#xff0c;我们看到微软的云服务…

常用MIME类型(Flv,Mp4的mime类型设置)

也许你会在纳闷&#xff0c;为什么我上传了flv或MP4文件到服务器&#xff0c;可输入正确地址通过http协议来访问总是出现“无法找到该页”的404错误呢&#xff1f;这就表明mp4格式文件是服务器无法识别的&#xff0c;其实&#xff0c;这是没有在iis中将相应的MIME类型进行设置的…

史上四大“杀人”建筑,烧掉几百亿,却犯低级错误,网友:有钱人的智商,我不懂

全世界只有3.14 % 的人关注了爆炸吧知识本文转自普象工业设计小站这个世界上有多少荒谬的建筑&#xff1f;国内土味审美的大楼只是冰山一角在更多的国家里耗费巨资&#xff0c;却无比失败的工程比土味大楼更多更荒唐你也许听说过美国这座会跳舞的大桥耗资千万&#xff0c;由优秀…

迎.NET6,今日先送200P全新视频教程 / 源码

昨晚已经正式发布了.NET6,具体参见&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-net-6/。这是.NET团队和社区努力一年的成果&#xff0c;C# 10 和 F# 6 有很大的语言改进&#xff0c;使代码更简单、更好。性能有了巨大的提升&#xff0c;我们看到微软的云服务…

android 横向stepview,Android 流程指示器 StepView

软件介绍StepView 是Android平台下自定义的流程指示器。效果图&#xff1a;使用:android:id"id/step_view0"android:layout_width"match_parent"android:layout_height"wrap_content"android:layout_centerInParent"true"android:text…

58年前,这3个人在“撕逼”中拿下诺贝尔奖!一起研究DNA的女科学家却痛苦去世.........

全世界只有3.14 % 的人关注了爆炸吧知识人类的本质是双螺旋结构1953年4月25日&#xff0c;《自然》杂志发表了一篇不到千字的论文&#xff1a;消息一出&#xff0c;便掀起了科学界的轩然大波。因为这篇短小精悍的论文史无前例地揭示了正确的DNA立体结构。图片仅供参考&#xff…

Azure App Service 上的根证书

点击上方蓝字关注“汪宇杰博客”原文&#xff1a;Amol Mehrotra翻译&#xff1a;Edi Wang导语App Service 有一个受信任的根证书列表&#xff0c;您不能在 App Service 的多租户版本中修改这些证书&#xff0c;但您可以在应用服务环境 (ASE) 的受信任根存储中加载自己的 CA 证书…

jettytomcat对待表单过长问题

为什么80%的码农都做不了架构师&#xff1f;>>> 结论两句话&#xff1a; tomcat知道自己处理不了了&#xff0c;什么也不干过去了 jett知道自己处理不了了&#xff0c;抛个IllegalStateException出来通知一下 jetty默认允许的content-length2001000 org.eclips…