Мы используем клиенты, отличные от .NET, для интеграции некоторых наших общедоступных API с серверными службами .NET с использованием взаимодействия MassTransit, и периодически мы получаем запросы, которые не соответствуют спецификации сообщения (например, клиент предоставляет строку, в которой мы ожидать объект). Это приводит к ArgumentException
s в очереди xxx_error, как и ожидалось, и я вижу исходное сообщение с ошибочными свойствами. Я надеялся, что есть способ использовать и обрабатывать их, чтобы я мог «вручную» десериализовать их, проверять некоторые их свойства и уведомлять клиентов о том, что они отправляют ошибочные сообщения.
Я попытался использовать потребителя, который обрабатывает Fault<T>
для типа сообщения, однако, похоже, он также полагается на успешную десериализацию сообщения и никогда не вызывается. Сообщения, которые меня интересуют, имеют общий базовый интерфейс, и я попытался создать потребитель ошибок для этого интерфейса, надеясь обойти десериализацию, но этот потребитель ошибок, похоже, никогда не вызывается.
Я также пытался использовать неуниверсального Fault
потребителя, который действительно получает сообщение, однако, если я использую этого потребителя, у меня нет возможности добраться до исходного ошибочного сообщения, чтобы я мог его проверить. Если я проверяю содержимое сообщения, используя context.ReceiveContext.GetBody()
, оно имеет свойства сообщения об ошибке и исключение, но не детали исходного сообщения. Следовательно, использование либо TryGetMessage()
, либо TryGetPayload()
возвращает false.
Пример кода для иллюстрации того, о чем я говорю:
// base interface
public interface IFoo {
Bar Bar { get; }
}
// message class
public class Foo : IFoo {
public Bar Bar { get; set; }
}
// foo fault consumer - never called
public class FooFaultConsumer : IConsumer<Fault<Foo>>
{
public Task Consume(ConsumeContext<Fault<Foo>> context) => context.CompleteTask;
}
// ifoo fault consumer - also never called
public class FooInterfaceFaultConsumer : IConsumer<Fault<IFoo>>
{
public Task Consume(ConsumeContext<Fault<IFoo>> context) => context.CompleteTask;
}
// non-generic fault consumer - called but no details available
public class FaultConsumer : IConsumer<Fault>
{
public Task Consume(ConsumeContext<Fault> context)
{
context.TryGetMessage(out IFoo _); // returns false
context.TryGetMessage(out Foo _); // returns false
context.TryGetPayload(out IFoo _); // returns false
context.TryGetPayload(out Foo _); // returns false
context.ReceiveContext.TryGetPayload(out IFoo _); // returns false
context.ReceiveContext.TryGetPayload(out Foo _); // returns false
var body = Encoding.UTF8.GetString(context.ReceiveContext.GetBody());
// body is a JSON string with a message property that only has Fault properties
// it does not contain any of the original message properties
}
}
// registrations
cfg.ReceiveEndpoint(e => {
e.Consumer<FooFaultConsumer>();
e.Consumer<FooInterfaceFaultConsumer>();
});
Пример ошибочного сообщения, в котором Bar является строкой, а не объектом (сообщение упаковано в заголовки MassTransit).
{
"bar" : "foobar"
}
Есть ли элегантный способ обработки ошибок десериализации JSON и получения копии сообщения в том виде, в котором оно появляется в очереди ошибок? Я отмечаю, что документация MassTransit не рекомендует помещать потребителей в эти очереди.