Exemple #1
0
        /// <summary>
        /// Get the fully qualified error code from the http response message, if exists
        /// </summary>
        /// <param name="response">The http response message</param>
        /// <returns>The fully qualified error code, or the response status code if no error code was provided.</returns>
        public static async Task <ErrorCode> GetExceptionCodeAsync(HttpResponseMessage response)
        {
            // First we will attempt to retrieve the error code from the response content.
            string responseContentStr = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            // There are two things to consider when surfacing service errors to the user, the 6-digit error code and the code description. Ideally, when a backend service
            // returns an error, both of these fields are set in the same place. However, IoT Hub is returning the 6-digit code in the response content, while
            // the error description in the response header. Therefore, there is a chance that the 6-digit error code does not match the error description. For that reason,
            // the SDK will do its best to decide what to surface to the user.
            // The SDK will attempt to retrieve the integer error code from the response content and the error description from the response header. Through a 'description'
            // to 'error code' enum mapping, the SDK will check if both values are a match. If so, the SDK will populate the exception with the proper Code. In the case where
            // there is a mismatch between the error code and the description, the SDK returns ErrorCode.InvalidErrorCode and log a warning.

            int errorCode;

            try
            {
                IoTHubExceptionResult responseContent = JsonConvert
                                                        .DeserializeObject <IoTHubExceptionResult>(responseContentStr);
                Dictionary <string, string> messageFields = JsonConvert
                                                            .DeserializeObject <Dictionary <string, string> >(responseContent.Message);

                if (messageFields != null &&
                    messageFields.TryGetValue(CommonConstants.ErrorCode, out string errorCodeObj))
                {
                    errorCode = Convert.ToInt32(errorCodeObj, CultureInfo.InvariantCulture);
                }
                else
                {
                    return(ErrorCode.InvalidErrorCode);
                }
            }
            catch (JsonReaderException ex)
            {
                if (Logging.IsEnabled)
                {
                    Logging.Error(null, $"Failed to parse response content JSON: {ex}. Message body: '{responseContentStr}.'");
                }

                return(ErrorCode.InvalidErrorCode);
            }

            // Now that we retrieved the integer error code from the response content, we will retrieve the error description from the header.
            string headerErrorCodeString = response.Headers.GetFirstValueOrNull(CommonConstants.HttpErrorCodeName);

            if (headerErrorCodeString != null &&
                Enum.TryParse(headerErrorCodeString, out ErrorCode headerErrorCode))
            {
                if ((int)headerErrorCode == errorCode)
                {
                    // We have a match. Therefore, return the proper error code.
                    return(headerErrorCode);
                }

                if (Logging.IsEnabled)
                {
                    Logging.Error(null, $"There is a mismatch between the error code retrieved from the response content and the response header." +
                                  $"Content error code: {errorCode}. Header error code description: {(int)headerErrorCode}.");
                }
            }

            return(ErrorCode.InvalidErrorCode);
        }
Exemple #2
0
        /// <summary>
        /// Get the fully-qualified error code from the HTTP response message, if exists.
        /// </summary>
        /// <param name="response">The HTTP response message</param>
        /// <returns>The fully-qualified error code, or the response status code, if no error code was provided.</returns>
        public static async Task <ErrorCode> GetExceptionCodeAsync(HttpResponseMessage response)
        {
            // First we will attempt to retrieve the error code from the response content.
            string responseContentStr = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            // There are two things to consider when surfacing service errors to the user, the 6-digit error code and the code description. Ideally, when a backend service
            // returns an error, both of these fields are set in the same place. However, IoT hub is returning the 6-digit code in the response content, while
            // the error description in the response header. Therefore, there is a chance that the 6-digit error code does not match the error description. For that reason,
            // the SDK will do its best to decide what to surface to the user.
            // The SDK will attempt to retrieve the integer error code from the response content and the error description from the response header. Through a 'description'
            // to 'error code' enum mapping, the SDK will check if both values are a match. If so, the SDK will populate the exception with the proper Code. In the case where
            // there is a mismatch between the error code and the description, the SDK returns ErrorCode.InvalidErrorCode and log a warning.

            int errorCodeValue = (int)ErrorCode.InvalidErrorCode;

            try
            {
                IoTHubExceptionResult responseContent = JsonConvert.DeserializeObject <IoTHubExceptionResult>(responseContentStr);

                try
                {
                    Dictionary <string, string> messageFields = JsonConvert.DeserializeObject <Dictionary <string, string> >(responseContent.Message);

                    if (messageFields != null &&
                        messageFields.TryGetValue(CommonConstants.ErrorCode, out string errorCodeObj))
                    {
                        // The result of TryParse is not being tracked since errorCodeValue has already been initialized to a default value of InvalidErrorCode.
                        _ = int.TryParse(errorCodeObj, NumberStyles.Any, CultureInfo.InvariantCulture, out errorCodeValue);
                    }
                }
                catch (JsonReaderException ex)
                {
                    if (Logging.IsEnabled)
                    {
                        Logging.Error(null, $"Failed to deserialize error message into a dictionary: {ex}. Message body: '{responseContentStr}.'");
                    }

                    // In some scenarios, the error response string is a ';' delimited string with the service-returned error code.
                    const char errorFieldsDelimiter = ';';
                    string[]   messageFields        = responseContent.Message?.Split(errorFieldsDelimiter);

                    if (messageFields != null)
                    {
                        foreach (string messageField in messageFields)
                        {
#if NET451 || NET472 || NETSTANDARD2_0
                            if (messageField.IndexOf(CommonConstants.ErrorCode, StringComparison.OrdinalIgnoreCase) >= 0)
#else
                            if (messageField.Contains(CommonConstants.ErrorCode, StringComparison.OrdinalIgnoreCase))
#endif
                            {
                                const char errorCodeDelimiter = ':';

#if NET451 || NET472 || NETSTANDARD2_0
                                if (messageField.IndexOf(errorCodeDelimiter) >= 0)
#else
                                if (messageField.Contains(errorCodeDelimiter))
#endif
                                {
                                    string[] errorCodeFields = messageField.Split(errorCodeDelimiter);
                                    if (Enum.TryParse(errorCodeFields[1], out ErrorCode errorCode))
                                    {
                                        errorCodeValue = (int)errorCode;
                                    }
                                }
                            }
                            break;
                        }
                    }
                    else
                    {
                        if (Logging.IsEnabled)
                        {
                            Logging.Error(null, $"Failed to deserialize error message into a dictionary and could not parse ';' delimited string either: {ex}." +
                                          $" Message body: '{responseContentStr}.'");
                        }

                        return(ErrorCode.InvalidErrorCode);
                    }
                }
            }
            catch (JsonReaderException ex)
            {
                if (Logging.IsEnabled)
                {
                    Logging.Error(null, $"Failed to parse response content JSON: {ex}. Message body: '{responseContentStr}.'");
                }

                return(ErrorCode.InvalidErrorCode);
            }

            // Now that we retrieved the integer error code from the response content, we will retrieve the error description from the header.
            string headerErrorCodeString = response.Headers.GetFirstValueOrNull(CommonConstants.HttpErrorCodeName);
            if (headerErrorCodeString != null &&
                Enum.TryParse(headerErrorCodeString, out ErrorCode headerErrorCode))
            {
                if ((int)headerErrorCode == errorCodeValue)
                {
                    // We have a match. Therefore, return the proper error code.
                    return(headerErrorCode);
                }

                if (Logging.IsEnabled)
                {
                    Logging.Error(null, $"There is a mismatch between the error code retrieved from the response content and the response header." +
                                  $"Content error code: {errorCodeValue}. Header error code description: {(int)headerErrorCode}.");
                }
            }

            return(ErrorCode.InvalidErrorCode);
        }