-
Notifications
You must be signed in to change notification settings - Fork 64
Expand file tree
/
Copy pathResponseHandler.cs
More file actions
86 lines (79 loc) · 4.9 KB
/
Copy pathResponseHandler.cs
File metadata and controls
86 lines (79 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------
namespace Microsoft.Graph
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Serialization;
/// <summary>
/// Provides method(s) to deserialize raw HTTP responses into strong types.
/// </summary>
public class ResponseHandler<T> : IResponseHandler where T : IParsable, new()
{
private readonly IParseNodeFactory _jsonParseNodeFactory;
/// <summary>
/// Constructs a new <see cref="ResponseHandler{T}"/>.
/// </summary>
/// <param name="parseNodeFactory"> The <see cref="IParseNodeFactory"/> to use for response handling</param>
public ResponseHandler(IParseNodeFactory parseNodeFactory = null)
{
_jsonParseNodeFactory = parseNodeFactory ?? ParseNodeFactoryRegistry.DefaultInstance;
}
/// <summary>
/// Process raw HTTP response into requested domain type.
/// </summary>
/// <typeparam name="NativeResponseType">The type of the response</typeparam>
/// <typeparam name="ModelType">The type to return</typeparam>
/// <param name="response">The HttpResponseMessage to handle</param>
/// <param name="errorMappings">The errorMappings to use in the event of failed requests</param>
/// <returns></returns>
public async Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, ParsableFactory<IParsable>> errorMappings)
{
if (response is HttpResponseMessage responseMessage && responseMessage.Content != null && typeof(T).IsAssignableFrom(typeof(ModelType)))
{
await ValidateSuccessfulResponseAsync(responseMessage, errorMappings).ConfigureAwait(false);
using var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
var jsonParseNode = await _jsonParseNodeFactory.GetRootParseNodeAsync(responseMessage.Content.Headers?.ContentType?.MediaType?.ToLowerInvariant(), responseStream);
return (ModelType)(object)jsonParseNode.GetObjectValue<T>((parsable) => new T());
}
return default;
}
/// <summary>
/// Validates the HttpResponse message is a successful response. Otherwise, throws a ServiceException with the error information
/// present in the response body.
/// </summary>
/// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/> to validate</param>
/// <param name="errorMapping">The errorMappings to use in the event of failed requests</param>
private async Task ValidateSuccessfulResponseAsync(HttpResponseMessage httpResponseMessage, Dictionary<string, ParsableFactory<IParsable>> errorMapping)
{
if (httpResponseMessage.IsSuccessStatusCode)
return;
var statusCodeAsInt = (int)httpResponseMessage.StatusCode;
var statusCodeAsString = statusCodeAsInt.ToString();
using var responseStream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
var rootNode = await _jsonParseNodeFactory.GetRootParseNodeAsync(httpResponseMessage.Content.Headers?.ContentType?.MediaType?.ToLowerInvariant(), responseStream);
ParsableFactory<IParsable> errorFactory;
if (errorMapping == null ||
!errorMapping.TryGetValue(statusCodeAsString, out errorFactory) &&
!(statusCodeAsInt >= 400 && statusCodeAsInt < 500 && errorMapping.TryGetValue("4XX", out errorFactory)) &&
!(statusCodeAsInt >= 500 && statusCodeAsInt < 600 && errorMapping.TryGetValue("5XX", out errorFactory)))
{
// We don't have an error mapping available to match. So default to using the odata specification
throw new ServiceException(ErrorConstants.Codes.GeneralException, httpResponseMessage.Headers, (int)httpResponseMessage.StatusCode, await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
}
else
{
// Use the errorFactory to create an error response
var result = rootNode.GetObjectValue(errorFactory);
if (result is not Exception ex)
throw new ApiException($"The server returned an unexpected status code and the error registered for this code failed to deserialize: {statusCodeAsString}");
else
throw ex;
}
}
}
}