覆盖 odata 错误响应以便隐藏堆栈跟踪

override odata error response so stack trace is hidden

我正在尝试使用 Odata 正确设置项目,当用户发送格式不正确的请求时,我会在请求中收到完整的堆栈回溯。我想覆盖此错误消息,以便我可以使用自定义消息并省略堆栈 跟踪返回给用户的数据。

 {
    "error": {
        "code": "",
        "message": "The query specified in the URI is not valid. Could not find a property named 'namee' on type 'Edocs.API.Springboard.Core.Site'.",
        "details": [],
        "innererror": {
            "message": "Could not find a property named 'namee' on type 'Edocs.API.Springboard.Core.Site'.",
            "type": "Microsoft.OData.ODataException",
            "stacktrace": "   at Microsoft.OData.UriParser.EndPathBinder.GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode)\r\n   at Microsoft.OData.UriParser.EndPathBinder.BindEndPath(EndPathToken endPathToken)\r\n   at Microsoft.OData.UriParser.MetadataBinder.BindEndPath(EndPathToken endPathToken)\r\n   at Microsoft.OData.UriParser.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.UriParser.OrderByBinder.ProcessSingleOrderBy(BindingState state, OrderByClause thenBy, OrderByToken orderByToken)\r\n   at Microsoft.OData.UriParser.OrderByBinder.BindOrderBy(BindingState state, IEnumerable`1 orderByTokens)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseOrderByImplementation(String orderBy, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseOrderBy()\r\n   at Microsoft.AspNet.OData.Query.OrderByQueryOption.get_OrderByClause()\r\n   at Microsoft.AspNet.OData.Query.Validators.OrderByQueryValidator.Validate(OrderByQueryOption orderByOption, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.Query.OrderByQueryOption.Validate(ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.CreateAndValidateQueryOptions(HttpRequest request, ODataQueryContext queryContext)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.<>c__DisplayClass1_0.<OnActionExecuted>b__1(ODataQueryContext queryContext)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, Func`2 modelFunction, IWebApiRequestMessage request, Func`2 createQueryOptionFunction)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.OnActionExecuted(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, IWebApiRequestMessage request, Func`2 modelFunction, Func`2 createQueryOptionFunction, Action`1 createResponseAction, Action`3 createErrorAction)"
        }
    }
}

我假设此错误的包装发生在 [EnableQuery] 中,但是,我不确定我必须覆盖其中的哪个方法才能获得我想要的结果。

提前感谢您的帮助。

下面是我迄今为止尝试过的内容 我正在使用.net 5, Microsoft.AspNetCore.OData7.5.2 并且 Entity framework 用于数据检索。

到目前为止,我已经尝试使用 CustomEnableQueryAttribute As described in this question

    public class CustomEnableQueryAttribute : EnableQueryAttribute
{
    public override void ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)
    {
        try
        {
            base.ValidateQuery(request, queryOptions);
        }
        catch (ODataException e)
        {
           throw new  CustomException(e.Message, e) { UserMessage = "Invalid OData query." };
        }
    }

}


public class CustomException: ODataException
{
    public string UserMessage;
    public CustomException(string message, ODataException oData) : base(message)
    {
        
    }
}

这未能解决问题。

我也试过了:

public class CustomODataOutputFormatter : ODataOutputFormatter
{
    private readonly JsonSerializer serializer;
    private readonly bool isDevelopment;

    public CustomODataOutputFormatter(bool isDevelopment)
      : base(new[] { ODataPayloadKind.Error })
    {
        this.serializer = new JsonSerializer { ContractResolver = new CamelCasePropertyNamesContractResolver() };
        this.isDevelopment = isDevelopment;

        this.SupportedMediaTypes.Add("application/json");
        this.SupportedEncodings.Add(new UTF8Encoding());
    }

    public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        if (!(context.Object is SerializableError serializableError))
        {
            return base.WriteResponseBodyAsync(context, selectedEncoding);
        }

        var error = serializableError.CreateODataError(this.isDevelopment);
        using (var writer = new StreamWriter(context.HttpContext.Response.Body))
        {
            this.serializer.Serialize(writer, error);
            return writer.FlushAsync();
        }
    }
}

public static class CommonExtensions
{
    public const string DefaultODataErrorMessage = "A server error occurred.";

    public static ODataError CreateODataError(this SerializableError serializableError, bool isDevelopment)
    {
        // ReSharper disable once InvokeAsExtensionMethod
        var convertedError = SerializableErrorExtensions.CreateODataError(serializableError);
        var error = new ODataError();
        if (isDevelopment)
        {
            error = convertedError;
        }
        else
        {
            // Sanitise the exposed data when in release mode.
            // We do not want to give the public access to stack traces, etc!
            error.Message = DefaultODataErrorMessage;
            error.Details = new[] { new ODataErrorDetail { Message = convertedError.Message } };
        }

        return error;
    }

    public static ODataError CreateODataError(this Exception ex, bool isDevelopment)
    {
        var error = new ODataError();

        if (isDevelopment)
        {
            error.Message = ex.Message;
            error.InnerError = new ODataInnerError(ex);
        }
        else
        {
            error.Message = DefaultODataErrorMessage;
            error.Details = new[] { new ODataErrorDetail { Message = ex.Message } };
        }

        return error;
    }
}

并在启动时使用此方法class:

            app.UseExceptionHandler(appBuilder =>
        {
            appBuilder.Use(async (context, next) =>
            {
                var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
                if (error?.Error != null)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var response = error.Error.CreateODataError(false);
                    await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
                }

                // when no error, do next.
                else await next();
            });
        });

none 个导致错误被覆盖,

问题出在

public class CustomException: ODataException

因为ODataException继承自InvalidOperationException,而handledEnableQueryAttribute

中以特殊方式继承

如果你把它改成

public class CustomException: Exception

您稍后可以使用 app.UseExceptionHandler(appBuilder =>... 处理它。