Zaimplementowałem IErrorHandler do obsługi wyjątków autoryzacji generowanych w konstruktorze mojej resturacyjnej usługi WCF. Po wykryciu ogólnego wyjątku mój typ niestandardowy jest zwracany zgodnie z oczekiwaniami, ale nagłówek ContentType jest niepoprawny.IErrorHandler zwraca nieprawidłową treść wiadomości, gdy kod stanu HTTP jest 401 Nieautoryzowany
HTTP/1.1 500 Internal Server Error
Content-Type: application/xml;
...
{"ErrorMessage":"Error!"}
Jednak gdy obsługi błędów usiłuje powrócić 401 nieautoryzowany kod stanu HTTP treść wiadomości jest zastępowany do domyślnego typu ale nagłówku contenttype jest tak jak powinno być.
HTTP/1.1 401 Unauthorized
Content-Type: application/json;
...
{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}
Oczywiście coś tu jest nie tak, ale nie jestem pewien co.
Jak zaimplementować IErrorHandler w taki sposób, że zwraca mój niestandardowy typ w jsonie z poprawnymi nagłówkami?
BaseDataResponseContract obiektu:
[Serializable]
[DataContract(Name = "BaseDataResponseContract")]
public class BaseDataResponseContract
{
[DataMember]
public string ErrorMessage { get; set; }
} // end
Jest to obiekt chcę wrócić. Wszystkie inne obiekty w mojej aplikacji dziedziczą z tego obiektu. Podczas generowania wyjątku bardzo zależy nam na kodzie statusu HTTP i komunikacie o błędzie.
IErrorHandler Realizacja (rejestracja nie jest pokazany na zwięzłość):
namespace WebServices.BehaviorsAndInspectors
{
public class ErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
} // end
public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
{
// Create a new instance of the object I would like to return with a default message
var baseDataResponseContract = new BaseDataResponseContract { ErrorMessage = "Error!" };
// Get the outgoing response portion of the current context
var response = WebOperationContext.Current.OutgoingResponse;
// Set the http status code
response.StatusCode = HttpStatusCode.InternalServerError;
// If the exception is a specific type change the default settings
if (ex.GetType() == typeof(UserNotFoundException))
{
baseDataResponseContract.ErrorMessage = "Invalid Username!";
response.StatusCode = HttpStatusCode.Unauthorized;
}
// Create the fault message that is returned (note the ref parameter)
fault = Message.CreateMessage(version, "", baseDataResponseContract, new DataContractJsonSerializer(typeof(BaseDataResponseContract)));
// Tell WCF to use JSON encoding rather than default XML
var webBodyFormatMessageProperty = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, webBodyFormatMessageProperty);
// Add ContentType header that specifies we are using json
var httpResponseMessageProperty = new HttpResponseMessageProperty();
httpResponseMessageProperty.Headers[HttpResponseHeader.ContentType] = "application/json";
fault.Properties.Add(HttpResponseMessageProperty.Name, httpResponseMessageProperty);
} // end
} // end class
} // end namespace
IServiceBehavior Realizacja:
namespace WebServices.BehaviorsAndInspectors
{
public class ErrorHandlerExtensionBehavior : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
private IErrorHandler GetInstance()
{
return new ErrorHandler();
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } // end
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var errorHandlerInstance = GetInstance();
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(errorHandlerInstance);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } // end
} // end class
} // end namespace
Web.Config:
<system.serviceModel>
<services>
<service name="WebServices.MyService">
<endpoint binding="webHttpBinding" contract="WebServices.IMyService" />
</service>
</services>
<extensions>
<behaviorExtensions>
<!-- This extension if for the WCF Error Handling-->
<add name="ErrorHandlerBehavior" type="WebServices.BehaviorsAndInspectors.ErrorHandlerExtensionBehavior, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<ErrorHandlerBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
....
</system.serviceModel>
Wreszcie widzę podobne zachowanie podczas korzystania z wyjątku WebFaultException. Uważam, że jest to wynikiem niektórych głęboko zakopanych szantażystów .Net. Wybieram implementację IErrorHandler, aby móc wychwycić wszelkie inne wyjątki, które mogą nie być obsługiwane.
referencyjne:
https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx
innych przykładów:
IErrorHandler doesn't seem to be handling my errors in WCF .. any ideas?
How to make custom WCF error handler return JSON response with non-OK http code?
How do you set the Content-Type header for an HttpClient request?
Tak ... ale jak? Mam taki sam progrem z WebOperationContext.current.OutgoingResponse, ale nie mam w nim metody zapisu, więc jak stworzyć niestandardową odpowiedź? – DestyNova