有没有办法处理asp.net core odata错误

2024-05-20

有没有办法处理 asp.net core odata 错误?

我有一个模特班DimDateAvailable具有一个属性,主键为int DateId,然后我打电话/data/DimDateAvailable?$select=test.

其他调用按预期工作并返回我想要的内容 - 这是故意生成错误的调用,但它失败了,因为模型上没有名为 test 的属性。响应按预期返回,如下所示:{"error":{"code":"","message":"The query specified in the URI is not valid. Could not find a property named 'test' on type 'DimDateAvailable'...接下来是堆栈跟踪。

这个响应很好,当env.IsDevelopment() is true但我不想在不开发时公开堆栈跟踪。

我已经考虑过将代码包装在控制器中gettry-catch 中的方法,但我认为有一个操作过滤器对结果运行,因此它永远不会被调用。另一方面,我看不到在哪里注入任何中间件和/或添加任何过滤器来捕获错误。我怀疑可能有一种方法可以覆盖输出格式化程序来实现我想要的,但我不知道如何实现。

这是我目前所拥有的:

在 Startup.cs 中:

public void ConfigureServices(IServiceCollection services)
{
  services.AddScoped<TelemetryDbContext>();
  services.AddOData();
  services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  app.UseMvc(routeBuilder =>
  {
    routeBuilder.MapODataServiceRoute("odata", "data", GetEdmModel());
    routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(null).Count();

    // insert special bits for e.g. custom MLE here
    routeBuilder.EnableDependencyInjection();
  });
}

private static IEdmModel GetEdmModel()
{
  var builder = new ODataConventionModelBuilder();
  builder.EntitySet<DimDateAvailable>("DimDateAvailable");
  return builder.GetEdmModel();
}

在遥测 DbContext.cs 中:

public virtual DbSet<DimDateAvailable> DimDateAvailable { get; set; }

在 DimDateAvailable.cs 中

public class DimDateAvailable
{
  [Key]
  public int DateId { get; set; }
}

我的控制器:

public class DimDateAvailableController : ODataController
{
  private readonly TelemetryDbContext data;

  public DimDateAvailableController(TelemetryDbContext data)
  {
    this.data = data;
  }

  [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Supported, PageSize = 2000)]
  public IActionResult Get()
  {
    return Ok(this.data.DimDateAvailable.AsQueryable());
  }
}

这是一个带有 Microsoft.AspNetCoreOData v7.0.1 和 EntityFramework 6.2.0 包的 asp.net core 2 Web 应用程序。


调查伊哈尔的建议让我陷入了兔子洞,最后我插入了一个ODataOutputFormatter进入MVC选项进行拦截ODataPayloadKind.Error响应并重新格式化它们。

很有趣的是context.Features举行了一个实例IExceptionHandlerFeature in app.UseExceptionHandler()但不在ODataOutputFormatter。这种缺乏几乎是促使我首先提出这个问题的原因,但通过翻译解决了这个问题context.Object in the ODataOutputFormatter我在 OData 源中也看到了这一点。我不知道下面的更改在 asp.net core 中或使用 AspNetCoreOData 包时是否是良好实践,但它们现在满足我的要求。

Startup.cs 的更改

public void ConfigureServices(IServiceCollection services)
{
  services.AddScoped<TelemetryDbContext>();
  services.AddOData();
  services.AddMvc(options =>
  {
    options.OutputFormatters.Insert(0, new CustomODataOutputFormatter(this.Environment.IsDevelopment()));   
  });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  // Added this to catch errors in my own code and return them to the client as ODataErrors
  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(!env.IsDevelopment());
        await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
      }

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

  app.UseMvc(routeBuilder =>
  {
    routeBuilder.MapODataServiceRoute("odata", "data", GetEdmModel());
    routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(null).Count();

    // insert special bits for e.g. custom MLE here
    routeBuilder.EnableDependencyInjection();
  });
}

新类 CustomODataOutputFormatter.cs 和 CommonExtensions.cs

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;
  }
}

控制器的更改:

[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Supported, PageSize = 2000)]
public IQueryable<DimDateAvailable> Get()
{
  return this.data.DimDateAvailable.AsQueryable();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有办法处理asp.net core odata错误 的相关文章

随机推荐