Skip to content

Commit 0ceb853

Browse files
Refactor rest client ♻️
- Throws NotionApiException if there is any error while calling API - Allow options to configure header - Use the SendAsync and HttpRequestMessage to form the request
1 parent fff4ee3 commit 0ceb853

File tree

3 files changed

+138
-16
lines changed

3 files changed

+138
-16
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.IO;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Newtonsoft.Json;
5+
6+
namespace Notion.Client.Extensions
7+
{
8+
internal static class HttpResponseMessageExtensions
9+
{
10+
internal static async Task<T> ParseStreamAsync<T>(this HttpResponseMessage response, JsonSerializerSettings serializerSettings = null)
11+
{
12+
using (Stream stream = await response.Content.ReadAsStreamAsync())
13+
{
14+
using (StreamReader streamReader = new StreamReader(stream))
15+
{
16+
using (JsonReader jsonReader = new JsonTextReader(streamReader))
17+
{
18+
19+
JsonSerializer serializer = null;
20+
21+
if (serializerSettings == null)
22+
{
23+
serializer = JsonSerializer.CreateDefault();
24+
}
25+
else
26+
{
27+
serializer = JsonSerializer.Create(serializerSettings);
28+
}
29+
30+
return serializer.Deserialize<T>(jsonReader);
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Net;
3+
4+
namespace Notion.Client
5+
{
6+
class NotionApiException : Exception
7+
{
8+
public NotionApiException(HttpStatusCode statusCode, string message) : this(statusCode, message, null)
9+
{
10+
}
11+
12+
public NotionApiException(HttpStatusCode statusCode, string message, Exception innerException) : base(message, innerException)
13+
{
14+
Data.Add("StatusCode", statusCode);
15+
}
16+
}
17+
}

Src/Notion.Client/RestClient.cs

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,30 @@
33
using System.Net.Http;
44
using System.Net.Http.Headers;
55
using System.Text;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using Newtonsoft.Json;
9+
using Notion.Client.Extensions;
810
using Notion.Client.http;
911

1012
namespace Notion.Client
1113
{
1214
public interface IRestClient
1315
{
14-
Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null);
15-
Task<T> PostAsync<T>(string uri, object body);
16+
Task<T> GetAsync<T>(
17+
string uri,
18+
IDictionary<string, string> queryParams = null,
19+
IDictionary<string, string> headers = null,
20+
JsonSerializerSettings serializerSettings = null,
21+
CancellationToken cancellationToken = default);
22+
23+
Task<T> PostAsync<T>(
24+
string uri,
25+
object body,
26+
IDictionary<string, string> queryParams = null,
27+
IDictionary<string, string> headers = null,
28+
JsonSerializerSettings serializerSettings = null,
29+
CancellationToken cancellationToken = default);
1630
}
1731

1832
public class RestClient : IRestClient
@@ -21,7 +35,7 @@ public class RestClient : IRestClient
2135
private readonly ClientOptions _options;
2236
private readonly List<JsonConverter> jsonConverters = new List<JsonConverter>();
2337

24-
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
38+
private readonly JsonSerializerSettings defaultSerializerSettings = new JsonSerializerSettings
2539
{
2640
NullValueHandling = NullValueHandling.Ignore
2741
};
@@ -41,32 +55,89 @@ private static ClientOptions MergeOptions(ClientOptions options)
4155
};
4256
}
4357

44-
public async Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null)
58+
public async Task<T> GetAsync<T>(
59+
string uri,
60+
IDictionary<string, string> queryParams = null,
61+
IDictionary<string, string> headers = null,
62+
JsonSerializerSettings serializerSettings = null,
63+
CancellationToken cancellationToken = default)
4564
{
4665
EnsureHttpClient();
4766

48-
uri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
67+
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
4968

50-
using (var stream = await _httpClient.GetStreamAsync(uri))
69+
var response = await SendAsync(requestUri, HttpMethod.Get, headers, cancellationToken: cancellationToken);
70+
71+
if (response.IsSuccessStatusCode)
5172
{
52-
return SerializerHelper.Deserialize<T>(stream, jsonConverters);
73+
return await response.ParseStreamAsync<T>(serializerSettings);
5374
}
75+
76+
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
77+
? response.ReasonPhrase
78+
: await response.Content.ReadAsStringAsync();
79+
80+
throw new NotionApiException(response.StatusCode, message);
5481
}
5582

56-
public async Task<T> PostAsync<T>(string uri, object body)
83+
private async Task<HttpResponseMessage> SendAsync(
84+
string requestUri,
85+
HttpMethod httpMethod,
86+
IDictionary<string, string> headers = null,
87+
Action<HttpRequestMessage> attachContent = null,
88+
CancellationToken cancellationToken = default)
5789
{
58-
EnsureHttpClient();
90+
HttpRequestMessage httpRequest = new HttpRequestMessage(httpMethod, requestUri);
91+
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
92+
httpRequest.Headers.Add("Notion-Version", _options.NotionVersion);
93+
94+
if (headers != null)
95+
{
96+
AddHeaders(httpRequest, headers);
97+
}
98+
99+
attachContent?.Invoke(httpRequest);
100+
101+
return await _httpClient.SendAsync(httpRequest, cancellationToken);
102+
}
103+
104+
private static void AddHeaders(HttpRequestMessage request, IDictionary<string, string> headers)
105+
{
106+
foreach (var header in headers)
107+
{
108+
request.Headers.Add(header.Key, header.Value);
109+
}
110+
}
59111

60-
var content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, "application/json");
112+
public async Task<T> PostAsync<T>(
113+
string uri,
114+
object body,
115+
IDictionary<string, string> queryParams = null,
116+
IDictionary<string, string> headers = null,
117+
JsonSerializerSettings serializerSettings = null,
118+
CancellationToken cancellationToken = default)
119+
{
120+
EnsureHttpClient();
61121

62-
using (var response = await _httpClient.PostAsync(uri, content))
122+
void AttachContent(HttpRequestMessage httpRequest)
63123
{
64-
response.EnsureSuccessStatusCode();
124+
httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, defaultSerializerSettings), Encoding.UTF8, "application/json");
125+
}
126+
127+
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
65128

66-
var stream = await response.Content.ReadAsStreamAsync();
129+
var response = await SendAsync(requestUri, HttpMethod.Post, headers, AttachContent, cancellationToken: cancellationToken);
67130

68-
return SerializerHelper.Deserialize<T>(stream, jsonConverters);
131+
if (response.IsSuccessStatusCode)
132+
{
133+
return await response.ParseStreamAsync<T>(serializerSettings);
69134
}
135+
136+
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
137+
? response.ReasonPhrase
138+
: await response.Content.ReadAsStringAsync();
139+
140+
throw new NotionApiException(response.StatusCode, message);
70141
}
71142

72143
private HttpClient EnsureHttpClient()
@@ -75,8 +146,6 @@ private HttpClient EnsureHttpClient()
75146
{
76147
_httpClient = new HttpClient();
77148
_httpClient.BaseAddress = new Uri(_options.BaseUrl);
78-
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
79-
_httpClient.DefaultRequestHeaders.Add("Notion-Version", _options.NotionVersion);
80149
}
81150

82151
return _httpClient;

0 commit comments

Comments
 (0)