Nano Hash - криптовалюты, майнинг, программирование

Есть ли способ справиться с ошибками odata ядра asp.net

Есть ли способ справиться с ошибками odata ядра asp.net?

У меня есть класс модели 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() равно true, но я не хочу показывать трассировку стека, когда он не находится в разработке.

Я посмотрел, как заключить код в метод get контроллеров в try-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();
}

В TelemetryDbContext.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());
  }
}

Это веб-приложение asp.net core 2 с пакетами Microsoft.AspNetCoreOData v7.0.1 и EntityFramework 6.2.0.

24.07.2018

Ответы:


1

Исследование предложения Ихара привело меня к кроличьей норе, и в итоге я вставил ODataOutputFormatter в параметры MVC, чтобы перехватить ODataPayloadKind.Error ответы и переформатировать их.

Было интересно увидеть, что context.Features содержит экземпляр IExceptionHandlerFeature в app.UseExceptionHandler(), но не в ODataOutputFormatter. Этот недостаток в значительной степени и побудил меня задать этот вопрос, но он был решен путем перевода context.Object в ODataOutputFormatter, что я видел в исходном коде OData. Я не знаю, являются ли приведенные ниже изменения хорошей практикой в ​​ядре asp.net или при использовании пакета 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();
}
26.07.2018

2

Если вы хотите настроить ответы, включая настройку ответов об ошибках, попробуйте использовать ODataQueryOptions вместо использования

[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Supported, PageSize = 2000)]

Проверьте некоторые образцы на https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options.#invoking-query-options-direct

Это позволит вам кэшировать ошибки проверки и создавать собственный ответ.

26.07.2018

3

У меня была эта проблема в прошлом, и единственный способ заставить ее работать без написания промежуточного программного обеспечения:

Попробуй это:

    catch (ODataException ex)
    {
        HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;//This line is important, if not it will return 500 Internal Server Error.
        return BadRequest(ex.Message);//Just respond back the actual error which is 100% correct.
    }

Тогда ошибка будет выглядеть так:

{
    "@odata.context": "http://yourendpoint.com$metadata#Edm.String",
    "value": "The property 'test' cannot be used in the $select query option."
}

Надеюсь это поможет.

Спасибо

29.08.2019
  • Это казалось многообещающим, но в конечном итоге не обнаруживает ошибок, когда я помещаю это в свой метод действия ASP.NET Core. Похоже, что ошибка возникает до выполнения действия. Я думал, что это может произойти после выполнения действия из-за отложенной оценки IQueryable, но, похоже, это произошло до маршрутизации. 30.07.2020
  • Привет, @flipdoubt, вы можете попробовать использовать эту логику в настраиваемом промежуточном программном обеспечении, а затем попробовать поиграть с порядком вызова вашего промежуточного программного обеспечения, чтобы увидеть, что вы получите. 30.07.2020
  • Я поместил это в пользовательский EnableQueryAttribute, чтобы добиться желаемых результатов. Подробности на stackoverflow.com/a/63176502/470. 30.07.2020
  • где в списке настраиваемого промежуточного программного обеспечения вы бы поместили это настраиваемое промежуточное программное обеспечение? Между аутентификацией и маршрутизацией? 30.07.2020
  • Попробуйте его после проверки подлинности, чтобы предотвратить обработку логики настраиваемым промежуточным программным обеспечением до того, как произойдет проверка подлинности. 30.07.2020
  • Я пробовал это, но я не уверен, что делать, чтобы исключение возникло внутри промежуточного программного обеспечения. Если я просто заключу await next() в try/catch(ODataException), ошибка не появится до тех пор, пока не будет. 30.07.2020
  • Попробуйте обновить context.Response.StatusCode = StatusCodes.Status400BadRequest. Вы можете вставить HttpContext в свой InvokeAsync и попытаться сделать это при возникновении этого исключения. Сообщите мне, что будет после этого. 30.07.2020
  • Позвольте нам продолжить это обсуждение в чате. 30.07.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..