В OpenRasta, как вы должны обрабатывать ошибки или исключения кодеков? - PullRequest
0 голосов
/ 29 марта 2012

Мой сценарий таков:

  1. Клиентское приложение выполняет HTTP POST для конечной точки, предоставляемой OpenRasta.
  2. Тело запроса содержит ошибку, которая вызывает проблему в кодеке - это пользовательская реализация OpenRasta.Codecs.IMediaTypeReader. Это преобразует полезную нагрузку JSON в ожидаемое обработчиком POCO.
  3. Кодек выдает исключение, которое описывает ошибку полезным способом. Например: Newtonsoft.Json.JsonReaderException: After parsing a value an unexpected character was encountered: ". Line 4, position 5.
  4. Клиентское приложение получает HTTP 405 - MethodNotAllowed. Клиент не видит никаких деталей об исключениях.

Если кодек модифицирован, чтобы перехватить JsonReaderException и вернуть Missing.Value, аналогично Реализация кодека вики, тогда клиент получает HTTP 500 - Internal Server Error. В теле ответа также описывается следующее исключение:

System.InvalidOperationException: The operation is not ready for invocation.
   at OpenRasta.OperationModel.MethodBased.MethodBasedOperation.Invoke()
   at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.<Invoke>b__0()
   at OpenRasta.OperationModel.Interceptors.OperationWithInterceptors.Invoke()
   at OpenRasta.OperationModel.OperationExecutor.Execute(IEnumerable`1 operations)
   at OpenRasta.Pipeline.Contributors.OperationInvokerContributor.ExecuteOperations(ICommunicationContext context)
   at OpenRasta.Pipeline.PipelineRunner.ExecuteContributor(ICommunicationContext context, ContributorCall call)

Как мне изменить приложение так, чтобы:

  • Клиент получает неверный запрос HTTP 400.
  • Клиент получает строку, содержащую сведения об исключении, обнаруженном в кодеке.

Ответы [ 2 ]

3 голосов
/ 21 мая 2013

Вот небольшой вариант ответа выше - на этот раз с выбором кодека на основе данных результата операции.

В пределах IConfigurationSource:

using (OpenRastaConfiguration.Manual)
{
    ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>(); 

    ResourceSpace.Has.ResourcesOfType<ApplicationError>()
                 .WithoutUri
                 .TranscodedBy<ApplicationErrorCodec>();

                 // Or use a generic JSON serializer like this:
                 // .AsJsonDataContract();

    // Other configuration here
}

Теперь ErrorCheckingContributor выглядит так:

public class ErrorCheckingContributor : IPipelineContributor
{
  public void Initialize(IPipeline pipelineRunner)
  {
    pipelineRunner
        .Notify(CheckRequestDecoding)
        .After<KnownStages.IOperationResultInvocation>()
        .And.Before<KnownStages.ICodecResponseSelection>();
  }

  private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
  {
    if (context.ServerErrors.Count == 0)
    {
      return PipelineContinuation.Continue;
    }

    Error err = context.ServerErrors[0];

    // Get a suitable message (err.Message contains stack traces, so try to avoid that)
    string msg = err.Title;
    if (msg == null && err.Exception != null)
      msg = err.Exception.Message;
    if (msg == null)
      msg = err.Message;

    // Create instance of an error information resource which is specific for the application
    // - This one is rather simple and only contains a copy of the message
    ApplicationError error = new ApplicationError(msg);

    // Set operation result to be "400 Bad Request" and remove errors
    context.OperationResult = new OperationResult.BadRequest { ResponseResource = error };
    context.ServerErrors.Clear();

    // Render immediately without starting any handlers
    return PipelineContinuation.RenderNow;
  }
}

Класс ApplicationError:

public class ApplicationError
{
  public string Message { get; set; }

  public ApplicationError(string message)
  {
    Message = message;
  }
}

Наконец нам нужен кодек ApplicationErrorCodec для ApplicationError. Это не отличается от любого другого кодека IMediaTypeWriter, но во многом зависит от вашего ожидаемого типа носителя ответа. См. https://github.com/openrasta/openrasta/wiki/Implementing-a-Codec для одного примера.

2 голосов
/ 29 марта 2012

Найдя эту ветку в Группах Google, которая содержит все ответы, моя текущая реализация выглядит примерно так:

В моей реализации IConfigurationSource:

using (OpenRastaConfiguration.Manual)
{
    ResourceSpace.Uses.PipelineContributor<ErrorCheckingContributor>(); 

    // Other configuration here
}

Тогда ErrorCheckingContributor выглядит примерно так:

public class ErrorCheckingContributor : IPipelineContributor
{
    public void Initialize(IPipeline pipelineRunner)
    {
        pipelineRunner
            .Notify(CheckRequestDecoding)
            .After<KnownStages.IOperationResultInvocation>()
            .And.Before<KnownStages.ICodecResponseSelection>(); 
    }

    private static PipelineContinuation CheckRequestDecoding(ICommunicationContext context)
    {
        if (context.ServerErrors.Count == 0)
        {
            return PipelineContinuation.Continue;
        }

        var first = context.ServerErrors[0];
        if (first.Exception is Newtonsoft.Json.JsonReaderException)
        {
            context.Response.Entity.ContentType = MediaType.TextPlain;
            context.Response.Entity.ContentLength = first.Exception.Message.Length;
            using (var sw = new StreamWriter(context.Response.Entity.Stream))
            {
                sw.Write(first.Exception.Message);
            }
        }

        return PipelineContinuation.Continue;
    } 
}

Есть некоторые вещи, о которых следует помнить:

  • Если обработчик должен бросить JsonReaderException, это также будет обработано здесь.
  • Он не проверяет, какие типы носителей принимает клиент.Это отличается от исключений, выдаваемых обработчиками, которые проходят выбор кодека.
  • Пробная установка context.OperationResult в context.ServerErrors - но не проходит через кодек.
...