起因
项目中使用了异常中间件捕获,但是在一个接口请求中手动抛出的异常无法被捕获,程序直接关闭了
业务解释
在一个登录功能中,有短信验证码登录和密码登录,两个方法类似,只有验证逻辑有差别。
所以对两个方法进行了封装,该封装需要传入一个委托Action,无返回值。
两个接口只需要调用该方法并传入不同的验证逻辑即可,如果验证不通过在验证逻辑中手动抛异常由异常中间件统一捕获。
发现问题
在调用密码登录接口时,因为只需要验证密码,不需要调用其他组件,所以没有使用async/await,异常是可以被正常捕获的
在调用短信验证码登录接口时,需要调用验证码组件进行验证,使用了async/await,异常无法被捕获
代码示例
//封装代码
async Task<ApiResult> LoginAsync(Users users, Action<Users, Users> action)
{
//一些逻辑
action.Invoke(users, entity);
//一些逻辑
return ApiResult<object>.OK();
}
//密码登录
public async Task<ApiResult> LoginByPwdAsync(Users users)
{
return await LoginAsync(users, (args, entity) =>
{
throw new KnowException(ApiResult.ClientError("密码错误~"));
});
}
//验证码登录
public async Task<ApiResult> LoginBySmsCodeAsync(string sms_code, Users users)
{
return await LoginAsync(users, async (args, entity) =>
{
throw new KnowException(ApiResult.ClientError("验证码错误~"));//该异常无法被捕获
});
}
原因
原因很简单, Action 是不支持异步调用的, Action 委托表示一个不返回值的方法。由于 Action 本身是同步的,它不能直接处理异步操作。当你在一个 Action 委托中使用 async 和 await 关键字时,编译器会生成一个返回 Task 的方法,而这与 Action 的签名不匹配。
换成 Func 即可,如果没有返回值,则使用 Func<Task>
更改后的代码
//封装代码
async Task<ApiResult> LoginAsync(Users users, Func<Users, Users, Task> action)//Action 改成 Func,并返回Task,这个很重要
{
//一些逻辑
action.Invoke(users, entity);
//一些逻辑
return ApiResult<object>.OK();
}
//密码登录
public async Task<ApiResult> LoginByPwdAsync(Users users)
{
return await LoginAsync(users,async (args, entity) =>
{
throw new KnowException(ApiResult.ClientError("密码错误~"));
await Task.CompletedTask;
});
}
//验证码登录
public async Task<ApiResult> LoginBySmsCodeAsync(string sms_code, Users users)
{
return await LoginAsync(users, async (args, entity) =>
{
throw new KnowException(ApiResult.ClientError("验证码错误~"));//该异常无法被捕获
//此处需要调用验证码组件,所以使用了async
await Task.CompletedTask;
});
}