microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5c4075027232b28388ab1e973b9d2407df669d63

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.Common/Http/HttpClient.cs

192lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Net.Http.Headers;
5using System.Net.Http.Json;
6using System.Text.Json;
7using System.Text.Json.Serialization;
8
9using Microsoft.Teams.Common.Logging;
10
11namespace Microsoft.Teams.Common.Http;
12
13public interface IHttpClient : IDisposable
14{
15 public IHttpClientOptions Options { get; }
16
17 public Task<IHttpResponse<string>> SendAsync(IHttpRequest request, CancellationToken cancellationToken = default);
18 public Task<IHttpResponse<TResponseBody>> SendAsync<TResponseBody>(IHttpRequest request, CancellationToken cancellationToken = default);
19}
20
21public class HttpClient : IHttpClient
22{
23 public IHttpClientOptions Options { get; }
24
25 protected System.Net.Http.HttpClient _client;
26 protected ILogger _logger;
27
28 private readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions()
29 {
30 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
31 };
32
33 public HttpClient()
34 {
35 _client = new System.Net.Http.HttpClient();
36 _logger = new ConsoleLogger().Child("Http.Client");
37 Options = new HttpClientOptions();
38 Options.Apply(_client);
39 }
40
41 public HttpClient(IHttpClientOptions options)
42 {
43 _client = new System.Net.Http.HttpClient();
44 _logger = options.Logger?.Child("Http.Client") ?? new ConsoleLogger().Child("Http.Client");
45 Options = options;
46 Options.Apply(_client);
47 }
48
49 public HttpClient(System.Net.Http.HttpClient client)
50 {
51 _client = client;
52 _logger = new ConsoleLogger().Child("Http.Client");
53 Options = new HttpClientOptions();
54 Options.Apply(_client);
55 }
56
57 public async Task<IHttpResponse<string>> SendAsync(IHttpRequest request, CancellationToken cancellationToken = default)
58 {
59 var httpRequest = CreateRequest(request);
60 var httpResponse = await _client.SendAsync(httpRequest);
61 return await CreateResponse(httpResponse, cancellationToken);
62 }
63
64 public async Task<IHttpResponse<TResponseBody>> SendAsync<TResponseBody>(IHttpRequest request, CancellationToken cancellationToken = default)
65 {
66 var httpRequest = CreateRequest(request);
67 var httpResponse = await _client.SendAsync(httpRequest, cancellationToken);
68 return await CreateResponse<TResponseBody>(httpResponse, cancellationToken);
69 }
70
71 public void Dispose()
72 {
73 _client.Dispose();
74 }
75
76 protected HttpRequestMessage CreateRequest(IHttpRequest request)
77 {
78 var httpRequest = new HttpRequestMessage(
79 request.Method,
80 request.Url
81 );
82
83 Options.Apply(httpRequest);
84
85 if (request.Body is not null)
86 {
87 if (request.Body is string stringBody)
88 {
89 httpRequest.Content = new StringContent(stringBody);
90 }
91 else if (request.Body is IEnumerable<KeyValuePair<string, string>> dictionaryBody)
92 {
93 httpRequest.Content = new FormUrlEncodedContent(dictionaryBody);
94 }
95 else
96 {
97 string body = JsonSerializer.Serialize(request.Body, _jsonSerializerOptions);
98 httpRequest.Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json");
99 }
100 }
101
102 foreach (var kv in request.Headers)
103 {
104 if (kv.Key.StartsWith("Content-") && httpRequest.Content != null)
105 {
106 if (kv.Key == "Content-Type")
107 {
108 httpRequest.Content.Headers.ContentType = new MediaTypeHeaderValue(kv.Value.First());
109 continue;
110 }
111
112 httpRequest.Content.Headers.TryAddWithoutValidation(kv.Key, kv.Value);
113 continue;
114 }
115
116 httpRequest.Headers.TryAddWithoutValidation(kv.Key, kv.Value);
117 }
118 return httpRequest;
119 }
120
121 protected async Task<IHttpResponse<string>> CreateResponse(HttpResponseMessage response, CancellationToken cancellationToken = default)
122 {
123 if (!response.IsSuccessStatusCode)
124 {
125 var errorBody = await ParseErrorBody(response);
126
127 throw new HttpException()
128 {
129 Headers = response.Headers,
130 StatusCode = response.StatusCode,
131 Body = errorBody,
132 Request = response.RequestMessage
133 };
134 }
135
136 var body = await response.Content.ReadAsStringAsync() ?? throw new ArgumentNullException();
137
138 return new HttpResponse<string>()
139 {
140 Body = body,
141 Headers = response.Headers,
142 StatusCode = response.StatusCode
143 };
144 }
145
146 protected async Task<IHttpResponse<TResponseBody>> CreateResponse<TResponseBody>(HttpResponseMessage response, CancellationToken cancellationToken = default)
147 {
148 if (!response.IsSuccessStatusCode)
149 {
150 var errorBody = await ParseErrorBody(response);
151
152 throw new HttpException()
153 {
154 Headers = response.Headers,
155 StatusCode = response.StatusCode,
156 Body = errorBody,
157 Request = response.RequestMessage,
158 };
159 }
160
161 var body = await response.Content.ReadFromJsonAsync<TResponseBody>(cancellationToken) ?? throw new ArgumentNullException();
162
163 return new HttpResponse<TResponseBody>()
164 {
165 Body = body,
166 Headers = response.Headers,
167 StatusCode = response.StatusCode
168 };
169 }
170
171 private async Task<object> ParseErrorBody(HttpResponseMessage response)
172 {
173 var content = await response.Content.ReadAsStringAsync() ?? throw new ArgumentNullException();
174 object errorBody = content;
175
176 try
177 {
178 var bodyAsJson = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(content);
179
180 if (bodyAsJson is not null)
181 {
182 errorBody = bodyAsJson;
183 }
184 }
185 catch
186 {
187 // content is probably not a valid json
188 }
189
190 return errorBody;
191 }
192}