全局错误处理服务端

微软已经实施了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 730 统计不同回文子字符串(动态规划)

    730. 统计不同回文子字符串 给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模. 通过从 S 中删除 0 个或多个字符来获得子字符序列. 如果一个字符 ...

  2. Java实现判断单联通(强连通缩点+拓扑排序)Going from u to v or from v to u

    Description In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has ...

  3. ASP.NET Core Blazor Webassembly 之 路由

    web最精妙的设计就是通过url把多个页面串联起来,并且可以互相跳转.我们开发系统的时候总是需要使用路由来实现页面间的跳转.传统的web开发主要是使用a标签或者是服务端redirect来跳转.那今天来 ...

  4. 温故知新-多线程-深入刨析park、unpark

    文章目录 摘要 park.unpark 看一下hotspot实现 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | ...

  5. Java编程技术之浅析JVM内存

    JVM JVM->Java Virtual Machine:Java虚拟机,是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. 基本认知: ...

  6. 1.Go 开始搞起

    link 1. IDE Go Land 服务器激活 2. 资源 中文网站 翻译组 翻译组wiki 待认领文章 入门指南 中文文档 fork 更新 github 中如何定期使用项目仓库内容更新自己 fo ...

  7. 0.大话Spring Cloud

    天天说Spring cloud ,那到底它是什么? 定义 它不是云计算解决方案 它是一种微服务开发框架 它是(快速构建分布式系统的通用模式的)工具集 它基于Spring boot 构建开发 它是云原生 ...

  8. Python:列表和列表的增删改查

    目录 列表 列表的取值 通过下标 切片 用len()取得列表的长度 用下标改变列表中的值 列表连接和列表复制 用 del 语句从列表中删除值 in和not in操作符 多重赋值 列表的方法 查找 增加 ...

  9. TensorFlow从0到1之TensorBoard可视化数据流图(8)

    TensorFlow 使用 TensorBoard 来提供计算图形的图形图像.这使得理解.调试和优化复杂的神经网络程序变得很方便.TensorBoard 也可以提供有关网络执行的量化指标.它读取 Te ...

  10. 如何从二进制文件中读取int型序列

    使用的主要函数是int.from_bytes 代码如下: f = open('./T26.dat', 'rb') for i in range(20): A = f.read(2) A = int.f ...