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