全局错误处理服务端

微软已经实施了Interceptors,它们类似于FilterMiddlewares在ASP.NET MVC的核心或的WebAPI,它们可以用于全局异常处理,日志记录,验证等。

这是服务器端Interceptor自己的实现,Continuation是必须等待的Task,然后,如果引发了任何异常,则可以根据所获得的异常来控制RpcException和关联的StatusCode

using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System;
using System.Data.SqlClient;
using System.Threading.Tasks; namespace DemoGrpc.Web.Logging
{
public class LoggerInterceptor : Interceptor
{
private readonly ILogger<LoggerInterceptor> _logger; public LoggerInterceptor(ILogger<LoggerInterceptor> logger)
{
_logger = logger;
} public async override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
LogCall(context);
try
{
return await continuation(request, context);
}
catch (SqlException e)
{
_logger.LogError(e, $"An SQL error occured when calling {context.Method}");
Status status; if (e.Number == -2)
{
status = new Status(StatusCode.DeadlineExceeded, "SQL timeout");
}
else
{
status = new Status(StatusCode.Internal, "SQL error");
}
throw new RpcException(status);
}
catch (Exception e)
{
_logger.LogError(e, $"An error occured when calling {context.Method}");
throw new RpcException(Status.DefaultCancelled, e.Message);
} } private void LogCall(ServerCallContext context)
{
var httpContext = context.GetHttpContext();
_logger.LogDebug($"Starting call. Request: {httpContext.Request.Path}");
}
}
}

注册方式如下

using DemoGrpc.Web.Logging;
using DemoGrpc.Web.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; namespace DemoGrpc.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services)
{
//注册GRpc全局异常捕获
services.AddGrpc(options =>
{
options.Interceptors.Add<LoggerInterceptor>();
});
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
} app.UseHttpsRedirection(); app.UseRouting(); app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<CountryGrpcService>(); endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
}
}
}

第二种方法也可以捕获到GRpc异常,但是写法比较粗糙。不推荐使用

using AutoMapper;
using DemoGrpc.Domain.Entities;
using DemoGrpc.Protobufs;
using DempGrpc.Services.Interfaces;
using Grpc.Core;
using System;
using System.Threading.Tasks; public class CountryGrpcService : CountryService.CountryServiceBase
{
private readonly ICountryService _countryService;
private readonly IMapper _mapper; public CountryGrpcService(ICountryService countryService, IMapper mapper)
{
_countryService = countryService;
_mapper = mapper;
} public override async Task<CountriesReply> GetAll(EmptyRequest request, ServerCallContext context)
{
try
{
var countries = await _countryService.GetAsync();
return _mapper.Map<CountriesReply>(countries);
}
catch (Exception e)
{
throw new RpcException(Status.DefaultCancelled, e.Message);
}
}
}

Rpc异常信息介绍如下

一个普通标题 一个普通标题
Aborted 操作被中止,通常是由于并发性问题,如顺序器检查失败、事务中止等。
AlreadyExists 试图创建的一些实体(例如,文件或目录)已经存在。
Cancelled 该操作被取消(通常由调用者取消)。
DataLoss 不可恢复的数据丢失或损坏。
DeadlineExceeded 操作完成前截止日期已过期。对于改变系统状态的操作,即使操作已经成功完成,也会返回此错误。例如,来自服务器的成功响应可能会延迟到截止日期过期。
FailedPrecondition 操作被拒绝,因为系统没有处于执行操作所需的状态。例如,要删除的目录可能是非空的,一个rmdir操作被应用到一个非目录,等等。
Internal 内部错误。表示底层系统期望的某些不变量被打破。
InvalidArgument 客户端指定了无效的参数。注意,这与FAILED_PRECONDITION不同。INVALID_ARGUMENT表示与系统状态无关的参数(例如格式不正确的文件名)。
NotFound 一些被请求的实体(例如,文件或目录)没有找到。
OK 成功返回
OutOfRange 操作尝试超过有效范围。例如,查找或读取文件的前端。
PermissionDenied 调用者没有权限执行指定的操作。PERMISSION_DENIED不能用于由于耗尽某些资源而导致的拒绝(对于那些错误,应该使用RESOURCE_EXHAUSTED)。如果无法识别调用者,则不能使用PERMISSION_DENIED(对于那些错误,则使用UNAUTHENTICATED)。
ResourceExhausted 某些资源已经耗尽,可能是每个用户的配额,或者可能是整个文件系统空间不足。
Unauthenticated 未认证/授权
Unavailable 该服务目前不可用。这很可能是一种暂时的情况,可以通过后退重新尝试来纠正。注意,重试非幂等操作并不总是安全的。
Unimplemented 此服务中未实现或不支持/启用操作。
Unknown 未知的错误。可能返回此错误的一个示例是,如果从另一个地址空间接收到的状态值属于此地址空间中未知的错误空间。如果api没有返回足够的错误信息,则可能会将其转换为此错误。

具体地址:https://grpc.github.io/grpc/csharp/api/Grpc.Core.StatusCode.html

RpcException有相对应的重载:具体如下,可以自定义异常返回的信息

全局错误处理客户端

客户端也可以通过拦截器处理错误(实现客户端事件,如AsyncUnaryCall):

using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks; namespace ConsoleAppGRPC.Logging
{
public class LoggerInterceptor : Interceptor
{
private readonly ILogger<LoggerInterceptor> _logger; public LoggerInterceptor(ILogger<LoggerInterceptor> logger)
{
_logger = logger;
} public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
LogCall(context.Method); var call = continuation(request, context); return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
} private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
_logger.LogDebug($"Response received: {response}");
return response;
}
catch (RpcException ex)
{
_logger.LogError($"Call error: {ex.Message}");
return default;
}
} private void LogCall<TRequest, TResponse>(Method<TRequest, TResponse> method) where TRequest : class where TResponse : class
{
_logger.LogDebug($"Starting call. Type: {method.Type}. Request: {typeof(TRequest)}. Response: {typeof(TResponse)}");
}
}
}

使用方法:

using DemoGrpc.Domain.Entities;
using DemoGrpc.Protobufs;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
using static DemoGrpc.Protobufs.CountryService;
using Microsoft.Extensions.Logging;
using ConsoleAppGRPC.Logging; namespace ConsoleAppGRPC
{
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddScoped<LoggerInterceptor>();
services.AddLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
}); services.AddGrpcClient<CountryServiceClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
}).AddInterceptor<LoggerInterceptor>(); var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<CountryServiceClient>();
var logger = provider.GetRequiredService<ILogger<Program>>(); var countries = (await client.GetAllAsync(new EmptyRequest()))?.Countries.Select(x => new Country
{
CountryId = x.Id,
Description = x.Description,
CountryName = x.Name
}).ToList(); if (countries != null)
{
logger.LogInformation("Found countries");
countries.ForEach(x => Console.WriteLine($"Found country {x.CountryName} ({x.CountryId}) {x.Description}"));
}
else
{
logger.LogDebug("No countries found");
}
}
}
}

得到的结果信息

结论

我们在本文中看到了如何全局处理错误。拦截器、RpcException、状态代码和返回信息的使用为我们提供了一定的灵活性,比如定制错误和向客户端发送相关错误的可能性。

GRpc异常处理Filter的更多相关文章

  1. 重新整理 .net core 实践篇—————grpc[三十三]

    前言 简单整理一下grpc. 正文 什么是grpc? 一个远程过程调用框架,可以像类一样调用远程方法. 这种模式一般来说就是代理模式,然后都是框架自我生成的. 由google 公司发起并开源,故而前面 ...

  2. static,你还敢用吗?

    我用火狐的HttpRequester测试开发组里一个同学发布的Web API接口,遇到了一个奇怪的问题. 我测试边界情况时,第一次调用响应的结果是正常的,但当再次及以后的请求时,却返回了异常“Syst ...

  3. 在MVC中处理异常的总结

    无论是桌面程序还是web程序,异常处理都是必须的. 一般的处理方式是, 捕获异常,然后记录异常的详细信息到文本文件或者数据库中.在Asp.net MVC中可以使用内建的filter——HandleEr ...

  4. Spring+Maven+Dubbo+MyBatis+Linner+Handlebars—Web开发环境搭建

    本文主要分三部分,分别是:后台核心业务逻辑.桥梁辅助控制和前台显示页面. 本Web开发环境综合了多种工具,包括Maven包管理与编译工具.Dubbo分布式服务框架.MyBatis数据持久化工具.Lin ...

  5. 【ASP.NET Core】处理异常(下篇)

    上一篇中,老周给大伙伴们扯了有关 ASP.NET Core 中异常处理的简单方法.按照老周的优良作风,我们应该顺着这个思路继续挖掘. 本文老周就不自量力地介绍一下如何使用 MVC Filter 来处理 ...

  6. 【ASP.NET Core】处理异常--转

    老周写的[ASP.NET Core]处理异常非常的通俗易懂,拿来记录下. 转自老周:http://www.cnblogs.com/tcjiaan/p/8461408.html 今天咱们聊聊有关异常处理 ...

  7. 扩展 IHttpModule

    上篇提到请求进入到System.Web后,创建完HttpApplication对象后会执行一堆的管道事件,然后可以通过HttpModule来对其进行扩展,那么这篇文章就来介绍下如何定义我们自己的mod ...

  8. WebApi简介

    简单创建.NET Core WebApi:https://www.cnblogs.com/yanbigfeg/p/9197375.html 登陆验证四种方式:https://www.cnblogs.c ...

  9. Spring Cloud 系列之 Netflix Zuul 服务网关

    什么是 Zuul Zuul 是从设备和网站到应用程序后端的所有请求的前门.作为边缘服务应用程序,Zuul 旨在实现动态路由,监视,弹性和安全性.Zuul 包含了对请求的路由和过滤两个最主要的功能. Z ...

随机推荐

  1. Java实现 LeetCode 508 出现次数最多的子树元素和

    508. 出现次数最多的子树元素和 给出二叉树的根,找出出现次数最多的子树元素和.一个结点的子树元素和定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身).然后求出出现次数最多的子树元素和. ...

  2. 第九届蓝桥杯JavaB组省赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.第几天 题目描述 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数 ...

  3. java实现第四届蓝桥杯三部排序

    三部排序 题目描述 一般的排序有许多经典算法,如快速排序.希尔排序等. 但实际应用时,经常会或多或少有一些特殊的要求.我们没必要套用那些经典算法,可以根据实际情况建立更好的解法. 比如,对一个整型数组 ...

  4. Linux脚本安装包

    脚本安装包 并不是独立的软件安装包类型,常见安装的是源码包,是人为把安装过程写成了自动安装的脚本,只要执行脚本,定义简答的参数,就可以实现安装,非常类似于Windows下软件的安装方式. 安装过程(安 ...

  5. JavaScript如何调用Python后端服务

    本篇文章旨在通过一段JavaScript来演示如何调用python后端服务的,这是我开发的一个构建测试数据的工具. 第一部分:html 代码部分 第二部分:JavaScript代码部分 第三部分:Py ...

  6. 05-IntentFilter的匹配规则

    IntentFilter的匹配规则 原则上一个Intent不应该既是显示调用又是隐式调用,如果二者共存的话以显式调用为主 隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过 ...

  7. Redis学习笔记(十七) 集群(上)

    Redis集群是Redis提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移操作. 一个Redis集群通常由多个节点组成,在刚开始的时候每个节点都是相互独立的,他们处于一个只包含 ...

  8. 微信小程序生命周期,事件

    目录 双线程模型 小程序中 app.js 中的生命周期 小程序的页面的生命周期 小程序的事件 双线程模型 像 Vue 的双向数据绑定 总结: 在渲染层将wxml文件与wxss文件转成js对象,也就是虚 ...

  9. 使用Volo.Abp.MailKit发送邮件

    Volo.Abp.MailKit封装继承MailKit库,为Abp邮件发送提供了快捷实现. 邮箱配置 qq邮箱支持smtp功能,需要去申请开通.参考qq邮箱设置,最重要的是smtp发送邮件,qq邮箱对 ...

  10. electron-vue报错:Webpack ReferenceError: process is not defined

    electron-vue报错:Webpack ReferenceError: process is not defined 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总 ...