Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将gRPC C#服务器错误拦截器中捕获的异常发送到TypeScript gRPC Web客户端?_C#_Asp.net_Typescript_.net Core_Grpc - Fatal编程技术网

如何将gRPC C#服务器错误拦截器中捕获的异常发送到TypeScript gRPC Web客户端?

如何将gRPC C#服务器错误拦截器中捕获的异常发送到TypeScript gRPC Web客户端?,c#,asp.net,typescript,.net-core,grpc,C#,Asp.net,Typescript,.net Core,Grpc,我需要向客户端发送自定义异常消息。 我有以下代码: 在Startup.cs ConfigureServices方法中 services.AddGrpc(options=>options.Interceptors.Add()); 在ErrorInterceptor.cs中 public override异步任务UnaryServerHandler(TRequest请求、ServerCallContext上下文、UnaryServerMethod continuation) { 尝试 {

我需要向客户端发送自定义异常消息。 我有以下代码:

  • 在Startup.cs ConfigureServices方法中
services.AddGrpc(options=>options.Interceptors.Add());
  • 在ErrorInterceptor.cs中
public override异步任务UnaryServerHandler(TRequest请求、ServerCallContext上下文、UnaryServerMethod continuation)
{
尝试
{
返回等待继续(请求、上下文);
}
捕获(ValidationException ValidationException)
{
等待WriteResponseHeaderAsync(StatusCode.InvalidArgument,translation=>
GetEnumTranslation(validationXC.Error,validationXC.Parameters));
}
捕获(例外)
{
等待WriteResponseHeaderAsync(StatusCode.Internal,translation=>
translation.GetEnumTranslation(HttpStatusCode.InternalServerError));
}
返回默认值;
任务writeResponseHeaderAsync(状态码状态码,Func getMessage)
{
var httpContext=context.GetHttpContext();
var translationService=httpContext.RequestServices.GetService();
var errorMessage=getMessage(translationService);
var responseHeaders=新元数据
{
{nameof(errorMessage),errorMessage},//1)可以在浏览器的开发工具中看到,但不能在代码中看到
{“内容类型”,errorMessage},//2)难看,但可以工作
};
context.Status=新状态(statusCode,errorMessage);//3)不工作
返回上下文。WriteResponseHeadersAsync(responseHeaders);//4)可选?
}
}
  • 在mask-http.service.ts中
this.grpcClient.add(请求,(错误,回复:MaskInfoReply)=>{
this.grpcBaseService.handleResponse(错误、回复、响应=>{
常量掩码=新掩码(response.id,response.name);
回调(掩码);
});
});
  • 在grpc-base.service.ts中
HandlerResponse(错误:ServiceError,
答复:{
toObject(includeInstance?:布尔):T;
},
func:(响应:T)=>void){
如果(错误){
const errorMessage=error.metadata.headersMap['content-type'][0];
这个.toasterService.openSnackBar(错误消息,“Ok”);
控制台错误(error);
返回;
}
const response=reply.toObject();
func(响应);
}
  • 我想使用状态(注释3)发送错误,但没有更改
  • 我想知道是否有其他方法可以不在响应头中发送它(注释4)
  • 我试图添加自定义响应标题(注释1),但在客户端代码中收到的唯一标题是“内容类型”,因此我决定覆盖它(注释2)

  • 我最近遇到了同样的死胡同,并决定这样做:

  • 创建错误模型:

    message ValidationErrorDto {
        // A path leading to a field in the request body.
        string field = 1;
    
        // A description of why the request element is bad.
        string description = 2;
    }
    
    message ErrorSynopsisDto {
      string traceTag = 1;
      repeated ValidationErrorDto validationErrors = 2;
    }
    
    public class OperationException : Exception
    {
        private readonly List<ValidationErrorDto> validationErrors = new();
        public bool HasValidationErrors => this.validationErrors.Count > 0;
    
        public OperationException(string traceTag) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag
                }.ToJson() // <- here goes that extension
            ) => ErrorTag = traceTag;
    
        public OperationException(
            string traceTag,
            List<ValidationErrorDto> validationErrors
        ) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag,
                    ValidationErrors = { validationErrors }
                }.ToJson() // <- here goes that extension again
            )
        {
            ErrorTag = traceTag;
            this.validationErrors = validationErrors;
        }
    }
    
  • 为将对象序列化为JSON的错误模型创建扩展:

    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    
    public static class ErrorSynopsisDtoExtension
    {
        public static string ToJson(this ErrorSynopsisDto errorSynopsisDto) =>
        JsonConvert.SerializeObject(
            errorSynopsisDto,
            new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            });
    }
    
  • 创建封装错误模型的自定义异常:

    message ValidationErrorDto {
        // A path leading to a field in the request body.
        string field = 1;
    
        // A description of why the request element is bad.
        string description = 2;
    }
    
    message ErrorSynopsisDto {
      string traceTag = 1;
      repeated ValidationErrorDto validationErrors = 2;
    }
    
    public class OperationException : Exception
    {
        private readonly List<ValidationErrorDto> validationErrors = new();
        public bool HasValidationErrors => this.validationErrors.Count > 0;
    
        public OperationException(string traceTag) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag
                }.ToJson() // <- here goes that extension
            ) => ErrorTag = traceTag;
    
        public OperationException(
            string traceTag,
            List<ValidationErrorDto> validationErrors
        ) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag,
                    ValidationErrors = { validationErrors }
                }.ToJson() // <- here goes that extension again
            )
        {
            ErrorTag = traceTag;
            this.validationErrors = validationErrors;
        }
    }
    
  • 最后,异常拦截器:

    public class ExceptionInterceptor : Interceptor
    {
        private readonly ILogger<ExceptionInterceptor> logger;
    
        public ExceptionInterceptor(ILogger<ExceptionInterceptor> logger) => this.logger = logger;
    
        public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
            TRequest request,
            ServerCallContext context,
            UnaryServerMethod<TRequest, TResponse> continuation
        )
        {
            try
            {
                return await continuation(request, context);
            }
            catch (OperationException ex)
            {
                this.logger.LogError(ex, context.Method);
                var httpContext = context.GetHttpContext();
    
                if (ex.HasValidationErrors)
                {
                    httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
                }
                else
                {
                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
                }
    
                throw;
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, context.Method);
                var httpContext = context.GetHttpContext();
                httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
                var opEx = new OperationException("MY_CUSTOM_INTERNAL_ERROR_CODE");
    
                throw new RpcException(
                    new Status(
                        StatusCode.Internal,
                        opEx.Message
                    )
                );
    
            }
        }
    }
    

    嗨,你发现了吗,我在同一个场景中,我需要这样做。
    public class OperationException : Exception
    {
        private readonly List<ValidationErrorDto> validationErrors = new();
        public bool HasValidationErrors => this.validationErrors.Count > 0;
    
        public OperationException(string traceTag) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag
                }.ToJson() // <- here goes that extension
            ) => ErrorTag = traceTag;
    
        public OperationException(
            string traceTag,
            List<ValidationErrorDto> validationErrors
        ) : base
            (
                new ErrorSynopsisDto
                {
                    TraceTag = traceTag,
                    ValidationErrors = { validationErrors }
                }.ToJson() // <- here goes that extension again
            )
        {
            ErrorTag = traceTag;
            this.validationErrors = validationErrors;
        }
    }
    
    throw new OperationException(
        "MY_CUSTOM_VALIDATION_ERROR_CODE",
        // the following block can be simplified with a mapper, for reduced boilerplate
        new()
        {
            new()
            {
                Field = "Profile.FirstName",
                Description = "Is Required."
            }
        }
    );
    
    public class ExceptionInterceptor : Interceptor
    {
        private readonly ILogger<ExceptionInterceptor> logger;
    
        public ExceptionInterceptor(ILogger<ExceptionInterceptor> logger) => this.logger = logger;
    
        public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
            TRequest request,
            ServerCallContext context,
            UnaryServerMethod<TRequest, TResponse> continuation
        )
        {
            try
            {
                return await continuation(request, context);
            }
            catch (OperationException ex)
            {
                this.logger.LogError(ex, context.Method);
                var httpContext = context.GetHttpContext();
    
                if (ex.HasValidationErrors)
                {
                    httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
                }
                else
                {
                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
                }
    
                throw;
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, context.Method);
                var httpContext = context.GetHttpContext();
                httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
                var opEx = new OperationException("MY_CUSTOM_INTERNAL_ERROR_CODE");
    
                throw new RpcException(
                    new Status(
                        StatusCode.Internal,
                        opEx.Message
                    )
                );
    
            }
        }
    }
    
    JSON.parse(err.message ?? {}) as ErrorSynopsisDto