/// <summary> /// Create a formatter for the specified contract type /// </summary> public static RestMessageDispatchFormatter CreateFormatter(Type contractType) { RestMessageDispatchFormatter retVal = null; if (!m_formatters.TryGetValue(contractType, out retVal)) { lock (m_formatters) { if (!m_formatters.ContainsKey(contractType)) { var typeFormatter = typeof(RestMessageDispatchFormatter <>).MakeGenericType(contractType); retVal = Activator.CreateInstance(typeFormatter) as RestMessageDispatchFormatter; m_formatters.Add(contractType, retVal); } } } return(retVal); }
/// <summary> /// Provide fault /// </summary> public bool ProvideFault(Exception error, RestResponseMessage faultMessage) { var uriMatched = RestOperationContext.Current.IncomingRequest.Url; while (error.InnerException != null) { error = error.InnerException; } var fault = new RestServiceFault(error); var authScheme = RestOperationContext.Current.AppliedPolicies.OfType <BasicAuthorizationAccessBehavior>().Any() ? "Basic" : "Bearer"; var authRealm = RestOperationContext.Current.IncomingRequest.Url.Host; // Formulate appropriate response if (error is DomainStateException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.ServiceUnavailable; } else if (error is ObjectLockedException lockException) { faultMessage.StatusCode = 423; fault.Data.Add(lockException.LockedUser); } else if (error is PolicyViolationException) { var pve = error as PolicyViolationException; if (pve.PolicyDecision == PolicyGrantType.Elevate) { // Ask the user to elevate themselves faultMessage.StatusCode = 401; faultMessage.AddAuthenticateHeader(authScheme, authRealm, "insufficient_scope", pve.PolicyId, error.Message); } else { faultMessage.StatusCode = 403; } } else if (error is SecurityException) { faultMessage.StatusCode = (int)HttpStatusCode.Forbidden; } else if (error is SecurityTokenException) { // TODO: Audit this faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized; var authHeader = $"Bearer realm=\"{RestOperationContext.Current.IncomingRequest.Url.Host}\" error=\"invalid_token\" error_description=\"{error.Message}\""; faultMessage.AddAuthenticateHeader(authScheme, authRealm, error: "invalid_token", description: error.Message); } else if (error is LimitExceededException) { faultMessage.StatusCode = (int)(HttpStatusCode)429; faultMessage.StatusDescription = "Too Many Requests"; faultMessage.Headers.Add("Retry-After", "1200"); } else if (error is AuthenticationException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized; faultMessage.AddAuthenticateHeader(authScheme, authRealm, "invalid_token", description: error.Message); } else if (error is UnauthorizedAccessException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Forbidden; } else if (error is SecuritySessionException ses) { switch (ses.Type) { case SessionExceptionType.Expired: case SessionExceptionType.NotYetValid: case SessionExceptionType.NotEstablished: faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized; faultMessage.AddAuthenticateHeader(authScheme, authRealm, error: "unauthorized"); break; default: faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Forbidden; break; } } else if (error is FaultException) { faultMessage.StatusCode = (int)(error as FaultException).StatusCode; } else if (error is Newtonsoft.Json.JsonException || error is System.Xml.XmlException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; } else if (error is DuplicateKeyException || error is DuplicateNameException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.Conflict; } else if (error is FileNotFoundException || error is KeyNotFoundException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.NotFound; } else if (error is DomainStateException) { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.ServiceUnavailable; } else if (error is DetectedIssueException) { faultMessage.StatusCode = (int)(System.Net.HttpStatusCode) 422; } else if (error is NotImplementedException) { faultMessage.StatusCode = (int)HttpStatusCode.NotImplemented; } else if (error is NotSupportedException) { faultMessage.StatusCode = (int)HttpStatusCode.MethodNotAllowed; } else if (error is PatchException) { faultMessage.StatusCode = (int)HttpStatusCode.Conflict; } else { faultMessage.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; } switch (faultMessage.StatusCode) { case 409: case 429: case 503: this.m_traceSource.TraceInfo("Issue on REST pipeline: {0}", error); break; case 401: case 403: case 501: case 405: this.m_traceSource.TraceWarning("Warning on REST pipeline: {0}", error); break; default: this.m_traceSource.TraceError("Error on REST pipeline: {0}", error); break; } RestMessageDispatchFormatter.CreateFormatter(RestOperationContext.Current.ServiceEndpoint.Description.Contract.Type).SerializeResponse(faultMessage, null, fault); AuditUtil.AuditNetworkRequestFailure(error, uriMatched, RestOperationContext.Current.IncomingRequest.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.IncomingRequest.Headers[o]), RestOperationContext.Current.OutgoingResponse.Headers.AllKeys.ToDictionary(o => o, o => RestOperationContext.Current.OutgoingResponse.Headers[o])); return(true); }