Abp vNext异常处理的缺陷/改造方案

之前吐槽Abp的用户/租户管理模块!今天我又来了,这次我给Abp官方repo提了一个issue。

目前Website使用Abp vNext开发,免不了要全局处理异常、提示服务器异常信息。

1. Abp官方异常处理

Abp项目默认会启动内置的异常处理,默认不将异常信息发送到客户端。
在AppModule文件ConfigureServices方法中使用以下代码:

Configure<AbpExceptionHandlingOptions>(options =>
{options.SendExceptionsDetailsToClients = true;
});

可将异常信息发送到客户端:如下图:

{
"error": {"code": null,"message": "ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n","details": "OdbcException: ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n\nSTACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81\n at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47\n at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332\n at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "42000] [Cloudera][ImpalaODBC] (360 "42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n\nSTACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81\n at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47\n at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332\n at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult") Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n\nSTACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81\n at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60\n at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47\n at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332\n at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n at Gridsum.EAP.Controllers.UserGroupController.UpdateUserGroupUserAsync(String id, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.HttpApi/Controllers/UserGroupController.cs:line 320\n at lambda_method3440(Closure , Object )\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n","data": null,"validationErrors": null}
}

经过几天倒腾,发现Abp vNext的异常处理有几个问题。

2.Abp异常处理存在的缺陷

  1. 并没有如官方所述:自动处理所有异常,实际需要满足官方所说的某个条件:

这就导致当Controller Action方法返回的不是object result时,则根本捕获不到异常(我们暂时不说middleware产生的异常),这应该算Abp的一个Bug

  1. 输出的异常没有TraceId, 不利于日志排查

  2. 发送到客户端的日志字段message,detail过于详细冗长,不适合前端显示

也可以配置SendExceptionsDetailsToClients,不将异常信息发送到客户端,但这样就因噎废食了。

3. 异常处理的目标

虽然Abp的异常处理有缺陷, 但只是异常信息应用上的缺陷,
Abp异常处理①对异常的划分、②异常信息的本地化、③出现异常时写日志  支持的还是相当好。

基于Abp的异常处理现状,考虑做一些改进:

  1. 对所有Controller-Action方法捕获异常, [修复Abp Bug]

  2. 在Abp的异常处理结果中添加 TraceId

  3. 希望将服务端异常分类,简化后给到前端;同时也不妨碍开发者查看详细异常信息。

4. 揪出Abp异常处理缺陷的根源

Abp异常处理的核心对象AbpExceptionFilter,实现IAsyncExceptionFilter过滤器, ITransientDependency瞬时注入接口。

这是一个ServiceFilterAttribute, 你可以理解有个特性作用在每一个Controller的Action方法上:

[ServiceFilter(typeof(AbpExceptionFilter))]

一旦某个Controller的Action方法发生异常, 会执行如下代码:

public async Task OnExceptionAsync(ExceptionContext context)
{if (!ShouldHandleException(context)){return;}await HandleAndWrapException(context);
}
  1. ShouldHandleException(context) :监测是否应该处理异常,据查该函数确实存在我上文说的问题:并不能捕获所有的Action方法的异常。

  2. HandleAndWrapException(context):异常处理步骤:

  • 根据Abp内置的异常类型,自动确定状态码 (这个在Abp官方文档有讲)

  • 序列化异常对象,并向客户端输出如下格式:

{
"error": {"code": null,"message": "ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n","details":xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......,"data": null,"validationErrors": null}
}

① 输出的信息,从一开始就没有包含TraceId;
② Abp标准格式化后的异常信息,过于冗长,messagedetails字段均不适合前端显示。

  • 写日志,默认异常级别为Error

掌握以上源码,我们可以针对性的改造Abp的核心异常处理类AbpExceptionFilter

5. Abp异常处理: 缺陷修复方案

光说不练假把式

Abp的AbpExceptionFilter不是抽象类,没法重载,为达到我们设定的3个目标。
考虑使用针对性的ExceptionFilter替换默认有缺陷的AbpExceptionFilter

①.  新建EapExceptionFilter,内容拷贝自AbpExceptionFilter, 并做出如下针对性修改:

②. 在AppModule中,替换默认的AbpExceptionFilter为新的EapExceptionFilter过滤器:

context.Services.AddMvc(options =>
{options.Filters.ReplaceOne(x=> (x as ServiceFilterAttribute)?.ServiceType?.Name==nameof(AbpExceptionFilter), new ServiceFilterAttribute(typeof(EapExceptionFilter)));  
})

改造的效果如下:

That's  All

如果大家真切使用了Abp vNext最新版,

相信我在第2点提到的Abp异常处理的缺陷,Abp使用者会感同身受;

第3点提出的几个目标也是企业级异常处理要解决的痛点。

此异常处理的思路也可推及到其他非Abp项目.

改造方案在Abp官方github issue上:  https://github.com/abpframework/abp/issues/6761

一家之言,如有其他看法,请不吝赐教!

(btw,公众号文章发布之后,限制修改;若有后续,请 [阅读原文]!)

Reference

  1. https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

  2. https://docs.abp.io/zh-Hans/abp/latest/Exception-Handling

  3. https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0

  • 吐槽一下Abp的用户和租户管理模块

  • 临近年关,修复ASP.NET Core因浏览器内核版本引发的单点登录故障

  • 一套标准的ASP.NET Core容器化应用日志收集分析方案

  • Oh my God, Swagger API文档竟然可以这样写?

  • ASP.NET Core应用注意这一点,CTO会对你刮目相看

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

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

相关文章

java原生的编译软件_Java 转原生平台代码 RoboVM

软件介绍编者注&#xff1a;RoboVM 项目已经关闭&#xff0c;目前有开源替代产品BugVM。RoboVM 编译器可以将 Java 字节码翻译成 ARM 或者 x86 平台上的原生代码&#xff0c;应用可直接在 CPU 上运行&#xff0c;无需其他解释器或者虚拟机。RoboVM 同时包含一个 Java 到 Object…

开源项目葫芦藤:IdentityServer4的实现及其运用

前言本篇文章主要是讲解葫芦藤项目中对IdentityServer的实践使用&#xff0c;为了使您对本篇文章中所讲述的内容有深刻的认识&#xff0c;并且在阅读时避免感到乏味&#xff0c;文中的内容不会涉及太多的基础理论知识&#xff0c;而更多的是采用动手实践的方式进行讲解&#xf…

facade java_Java设计模式之Facade模式

Java设计模式之Facade模式 GOF《设计模式》一书对Facade模式是这样描述的:为子系统中的一组接口提供一个统一接口。Facade模式定义了一个更高层的接口&#xff0c;使子系统更加容易使用。大致意思是说&#xff1a;使用一种比原有方式更简单的办法与系统交互。例如&#xff0c;…

如何在 ASP.NET Core Web API 中以三种方式返回数据

在 ASP.NET Core 中有三种返回 数据 和 HTTP状态码 的方式&#xff0c;最简单的就是直接返回指定的类型实例&#xff0c;如下代码所示&#xff1a;[ApiController][Route("[controller]")]public class WeatherForecastController : ControllerBase{[HttpGet]public …

java io 读取多个对象_Java IO系列(五):读写对象ObjectOutputStream和ObjectInputStream详解...

有必要多看几遍的关于字符和字节&#xff0c;例如文本文件&#xff0c;XML这些都是用字符流来读取和写入。而如RAR&#xff0c;EXE文件&#xff0c;图片等非文本&#xff0c;则用字节流来读取和写入。读写对象&#xff0c;传输对象在Java中很常使用&#xff0c;在javaBean中就经…

Dotnet Core多版本API共存的优雅实现

API升级&#xff0c;新旧版本的API共存&#xff0c;怎么管理呢&#xff1f;一、前言最近&#xff0c;单位APP做了升级&#xff0c;同步的&#xff0c;API也做了升级。升级过程中&#xff0c;出现了一点问题&#xff1a;API升级后&#xff0c;旧API也需要保留&#xff0c;因为有…

java 文件监听器_java7 文件监听器

java7中的文件监听&#xff0c;主要可以监听指定目录下的文件&#xff1a;新建 删除 修改等操作。StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE 这三个事件 被注册到watchService 对象中&#xff0c;之后…

Beetlex之websocket/tls服务压测工具

为了方便压力测试ws服务&#xff0c;Beetlex同样提供相关工具来对ws/wss服务的性能进行测试测试。安装可以访问https://github.com/beetlex-io/TCPBenchmarks 下载最新版本工作&#xff0c;工具可以运行在linux和windows系统&#xff0c;安装完成后通过浏览器访问相关服务进行操…

php判断数组下标,php判断json或者数组格式与给定格式是否一致

开发的时候碰到个问题&#xff0c;我们技术人员规定了录数据的格式&#xff0c;例如&#xff1a;我们的json模板如下&#xff1a;{"sentence":"Martin and Kelvin went to Chengduon holiday. They are ordering in a rest aurant now but cannot read the char…

php 设置excel格式,php 操作excel文件的方法小结

用php生成excel文件的方法&#xff0c;单独就是用的php,注意一下数据的输入方法即可。一、php&#xff0c;不用COM&#xff0c;生成excel文件header("Content-type:application/vnd.ms-excel");header("Content-Disposition:filenametest.xls");echo "…

大学班里80%都去培训班,为什么我没去

背景大四刚开始&#xff0c;各大培训机构就开始到各大高校开始宣传&#xff0c;我们学校也不例外&#xff1b;当时信息与计算科学这专业在我们学校并不景气&#xff0c;有好几次听说&#xff1a;如果我们那届的就职率还不行的话&#xff0c;那很大可能将此专业拿掉&#xff1b;…

织梦手机版list.php,解决织梦一级目录作域名list.php无法跳转到手机站的问题

最近测试了www.doweb8.com/m/list.php?tid1 在手机站访问的时候&#xff0c;无法跳转到手机的模板&#xff0c;终于找到了解决了方法&#xff0c;给大家分享一下修改根目录下的m/list.php文件重新弄的代码&#xff0c; 不区分几级目录&#xff0c;理论是根据你指定的PC&#…

如何在 C# 中使用 Buffer

缓冲区 是内存中的一组字节序列&#xff0c;缓冲 是用来处理落在内存中的数据&#xff0c;.NET 缓冲 指的是处理 非托管内存 中的数据&#xff0c;用 byte[] 来表示。当你想把数据写入到内存或者你想处理非托管内存中的数据&#xff0c;可以使用 .NET 提供的 System.Buffer类&a…

php强制对齐,[强迫症福利] 使用 PHPStorm 对齐数组的键值对

用 php-cs-fixer 自动格式化&#xff0c;一个配置配合 Git 自动格式化&#xff0c;补充楼上。示例 Git Hook忽略前端的代码格式化&#xff0c;修改一下就可以用。#!/bin/bash## check PHP code syntax error and standard with phpcs# https://blog.csdn.net/xsgnzb/article/de…

.NET SDK-Style 项目(Core、Standard、.NET5)中的版本号

.NET SDK-Style 项目&#xff08;Core、Standard、.NET5&#xff09;中的版本号独立观察员 2020 年 12 月 24 日之前 .NET Framework 时&#xff0c;项目版本号等信息是存储在 AssemblyInfo.cs 文件中&#xff0c;通过程序集特性进行设置&#xff1a;.NET Core 之后&#xff0c…

ajax php 动态,jQuery+PHP+Ajax实现动态数字统计展示功能

jQueryPHPAjax实现的一款动态数字统计展示实例&#xff0c;本例是在页面上动态展示了当前在线用户数&#xff0c;当然了&#xff0c;你可以应用到其他更多场景中。首先我们在#number放置要统计的数字&#xff1a;当前在线&#xff1a;然后我们要定义一个动画过程&#xff0c;使…

[Stardust]星尘分布式全链路监控

随着业务的发展&#xff0c;微服务系统会变得越来越大&#xff0c;各个服务之间的调用关系也会日趋复杂。一个WebApi请求&#xff0c;后方可能经历多个微服务以及数据库和MQ操作&#xff0c;在这个调用过程中&#xff0c;可能因为某一个服务节点出现延迟或者失败&#xff0c;而…

php去掉查询返回的字段序列,php数组函数序列之array_slice() - 在数组中根据条件取出一段值,并返回...

array_slice()定义和用法array_slice() 函数在数组中根据条件取出一段值&#xff0c;并返回。注释&#xff1a;如果数组有字符串键&#xff0c;所返回的数组将保留键名。(参见例子 4)语法array_slice(array,offset,length,preserve)参数 描述array 必需。规定输入的数组。offse…

如何在 C# 中使用 const,readonly,static

平时在开发时经常会用到 const,readonly,static 关键字&#xff0c;可以肯定这些关键词是完全不同的概念&#xff0c;但有时候他们在用法上很相似以至于在场景中不知道选择哪一个&#xff0c;这篇文章我们就来讨论 C# 中的 const&#xff0c;static 和 readonly 关键词&#xf…

php html asp .net iis tomcat,iis+apache+tomcat 整合共享80口 支持ASP .NET JSP PHP全能WEB服务...

文件目录说明&#xff1a;[apache-tomcat-6.0.37-windows-x86] tomcat 安装版与解压版文件[httpd-2.2] apache安装文件[java] jdk与jre安装文件[mysql] mysql与mysql-tool安装文件[php-5.2.14-Win32] php解压文件[tomcat-connectors-1.2.14-isapi-iis-6.0] isapi模块安装文件[t…