Мой подход состоит в том, чтобы постараться сделать его максимально простым. Вы не всегда гарантируете, что клиент будет знать, что делать с кодом ошибки HTTP, и поэтому я стараюсь и обязуюсь всегда обеспечивать, чтобы мои службы возвращали 200. Чтобы клиент мог знать о случившихся плохих вещах Я внедряю метаданные во все наши сервисные ответы. Рассмотрим следующую структуру:
[DataContract(Namespace = Constants.DataContractNamespace)]
public class ResponseMetadata : IExtensibleDataObject
{
[DataMember(Order = 1, Name = "HasError", IsRequired = true, EmitDefaultValue = true)]
public bool HasError { get; set; }
[DataMember(Order = 2, Name = "ServiceMessage", IsRequired = true, EmitDefaultValue = true)]
public string ServiceMessage { get; set; }
[DataMember(Order = 3, Name = "IdentityContext", IsRequired = true, EmitDefaultValue = true)]
public IdentityContext IdentityContext { get; set; }
[DataMember(Order = 4, Name = "Environment", IsRequired = true, EmitDefaultValue = true)]
public string Environment { get; set; }
[DataMember(Order = 5, Name = "Host", IsRequired = true, EmitDefaultValue = true)]
public string Host { get; set; }
[DataMember(Order = 6, Name = "Pulse", IsRequired = true, EmitDefaultValue = true)]
public string Pulse { get; set; }
public virtual ExtensionDataObject ExtensionData { get; set; }
}
Все наши типы ответов службы содержат ResponseMetadata как таковые:
[DataContract(Namespace = Constants.DataContractNamespace)]
public class EchoResponse
{
public EchoResponse(ResponseMetadata meta, string echo)
{
Metadata = meta;
EchoText = echo;
}
[DataMember(Order = 1)]
public ResponseMetadata Metadata { get; set; }
[DataMember(Order = 2)]
public string EchoText { get; set; }
}
Сейчас в реализации сервиса:
public EchoResponse GetEcho(string echo)
{
try
{
// Implement your service operation
var res = string.Empty;
var response = new EchoResponse(new ResponseMetadata(false, string.Empty, Pulse), res);
return response;
}
catch (WebException ex)
{
var err = StringHelper.FormatException(MethodBase.GetCurrentMethod(), ex, Pulse, true, ex.Status.ToString());
Log.Error(err, ex);
return new EchoResponse(new ResponseMetadata(true, err, Pulse), null);
}
catch (ArgumentOutOfRangeException ex)
{
var err = StringHelper.FormatException(MethodBase.GetCurrentMethod(), ex, Pulse, true);
Log.Warn(err);
return new EchoResponse(new ResponseMetadata(true, err, Pulse), null);
}
catch (Exception ex)
{
var err = StringHelper.FormatException(MethodBase.GetCurrentMethod(), ex, Pulse, true);
Log.Error(err, ex);
return new EchoResponse(new ResponseMetadata(true, err, Pulse), null);
}
}
Таким образом, как вы можете видеть, этот подход гарантирует, что любая ошибка или исключение будет поймано и обработано путем установки флага HasError и вставки значимого текста в ServiceMessage.
Структура IdentityContext используется для того, чтобы мои сервисные ответы всегда сообщали информацию об идентичности, а Pulse - это способ корреляции связанных операций. Я удалил код регистрации из примера.
Мы создали сотни сервисов с этой техникой для клиентов iPad, iPhone, WebSphere и .NET.