全局错误处理服务端

微软已经实施了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. undefined attribute name (XXXX)

    Window --> Preferences --> Web --> HTML Files --> Editor --> Validation --> Attrib ...

  2. (Java实现) N皇后问题

    n皇后问题是一个以国际象棋为背景的问题:在n×n的国际象棋棋盘上放置n个皇后,使得任何一个皇后都无法直接吃掉其他的皇后,即任意两个皇后都不能处于同一条横行.纵行或斜线上. 蛮力法思想: 解决n皇后问题 ...

  3. Java实现 LeetCode 554 砖墙(缝隙可以放在数组?)

    554. 砖墙 你的面前有一堵方形的.由多行砖块组成的砖墙. 这些砖块高度相同但是宽度不同.你现在要画一条自顶向下的.穿过最少砖块的垂线. 砖墙由行的列表表示. 每一行都是一个代表从左至右每块砖的宽度 ...

  4. Java中lang包的常用方法介绍

    JAVA API(图片来源:实验楼) 包装类 Integer包装类 方法 返回值 功能描述 byteValue() byte 以 byte 类型返回该 Integer 的值 intValue() in ...

  5. Java实现 LeetCode 11 盛最多水的容器

    11. 盛最多水的容器 给定 n 个非负整数 a1,a2,-,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) ...

  6. java实现第五届蓝桥杯奇怪的分式

    奇怪的分式 题目描述 上小学的时候,小明经常自己发明新算法.一次,老师出的题目是: 1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png) 老师刚想批 ...

  7. 【Vue】axios封装,更好的管理api接口和使用

    在现在的前端开发中,前后端分离开发比较主流,所以在封装方法和模块化上也是非常需要掌握的一门技巧.而axios的封装也是非常的多,下面的封装其实跟百度上搜出来的axios封装或者axios二次封装区别不 ...

  8. 分布式ID总结

    分布式ID 生成的ID使用场景 几乎所有的业务系统,都有生成一个记录标识的需求,例如:message_id, order_id.这个记录标识往往就是数据库中的唯一主键,数据库上会建立聚集索引(clus ...

  9. 【Spring注解驱动开发】使用@Lazy注解实现懒加载

    写在前面 Spring在启动时,默认会将单实例bean进行实例化,并加载到Spring容器中.也就是说,单实例bean默认在Spring容器启动的时候创建对象,并将对象加载到Spring容器中.如果我 ...

  10. python工业互联网应用实战2—从需求开始

    前言:随着国家工业2025战略的推进,工业互联网发展将会提速,将迎来一个新的发展时期,越来越多的企业开始逐步的把产线自动化,去年年底投产的小米亦庄的智能工厂就是一个热议的新闻.小米/华为智能工厂只能说 ...