全局错误处理服务端

微软已经实施了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 202 快乐数

    202. 快乐数 编写一个算法来判断一个数是不是"快乐数". 一个"快乐数"定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过 ...

  2. Java实现 蓝桥杯VIP 算法提高 棋盘多项式

      算法提高 棋盘多项式   时间限制:1.0s   内存限制:256.0MB 棋盘多项式 问题描述 八皇后问题是在棋盘上放皇后,互相不攻击,求方案.变换一下棋子,还可以有八车问题,八马问题,八兵问题 ...

  3. java实现第五届蓝桥杯LOG大侠

    LOG大侠 atm参加了速算训练班,经过刻苦修炼,对以2为底的对数算得飞快,人称Log大侠. 一天,Log大侠的好友 drd 有一些整数序列需要变换,Log大侠正好施展法力- 变换的规则是: 对其某个 ...

  4. 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'xxxx'中的标识列指定显式值

    执行以下sql INSERT INTO [Country] VALUES (, N'中国', N'China', N'CN'); 提示错误 仅当使用了列列表并且 IDENTITY_INSERT 为 O ...

  5. 实验三 Linux系统用户管理及VIM配置

    项目 内容 这个作业属于哪个课程 班级课程的主页链接 这个作业的要求在哪里 作业要求链接接地址 学号-姓名 17041428-朱槐健 作业学习目标  1.学习Linux系统用户管理 2.学习vim使用 ...

  6. ubuntu18.04安装qt时候的错误解决

    在ubuntu系统下安装好qt5.5后启动qtceator时提示: Qt5.5.1/Tools/QtCreator/lib/qtcreator/plugins/libHelp.so: 无法加载库Qt5 ...

  7. 01 . Docker原理部署及常用操作命令

    Docker的来源及构造: 容器是一种基础工具:泛指任何用于容纳其他物品的工具,可以部分或完全封闭,被用于容纳,储存,运输物品: 物品可以被放置在容器中,而容器可以保护内容物: 人类使用容器的历史有十 ...

  8. javaweb之Servlet,http协议以及请求转发和重定向

    本文是作者原创,版权归作者所有.若要转载,请注明出处. 一直用的框架开发,快连Servlet都忘了,此文旨在帮自己和大家回忆一下Servlet主要知识点.话不多说开始吧 用idea构建Servlet项 ...

  9. 如何打包发布加密的 Python 源代码

    这里介绍一种使用 PyInstaller 和 PyArmor 来发布加密 Python 源代码的方式,能够达到以下目的 把所有 Python 源代码打包成为可执行文件,客户不需要 Python 就可以 ...

  10. 动手造轮子:实现一个简单的 AOP 框架

    动手造轮子:实现一个简单的 AOP 框架 Intro 最近实现了一个 AOP 框架 -- FluentAspects,API 基本稳定了,写篇文章分享一下这个 AOP 框架的设计. 整体设计 概览 I ...