| | 1 | | using System.Net.Http.Headers; |
| | 2 | | using System.Text; |
| | 3 | | using System.Text.Json; |
| | 4 | |
|
| | 5 | | namespace Common.Core.Classes; |
| | 6 | |
|
| | 7 | | /// <summary>Base class for services consuming data from a RESTful API.</summary> |
| | 8 | | public class DataServiceBase |
| | 9 | | { |
| | 10 | | private readonly HttpClient _httpClient; |
| 32 | 11 | | private readonly string _mediaType = "application/json"; |
| | 12 | |
|
| | 13 | | #region Constructor and Initialization |
| | 14 | |
|
| | 15 | | /// <summary>Initializes a new instance of the DataServiceBase class.</summary> |
| | 16 | | /// <param name="baseUri">Base Uniform Resource Identifier.</param> |
| | 17 | | /// <param name="timeoutSeconds">The number of seconds to wait before a request times out.</param> |
| 32 | 18 | | public DataServiceBase( string baseUri, int timeoutSeconds = 100 ) |
| 32 | 19 | | { |
| 32 | 20 | | _httpClient = CreateSharedClient( baseUri, timeoutSeconds ); |
| 32 | 21 | | _httpClient.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( _mediaType ) ); |
| 32 | 22 | | } |
| | 23 | |
|
| | 24 | | #endregion |
| | 25 | |
|
| | 26 | | /// <summary>Deletes a resource.</summary> |
| | 27 | | /// <typeparam name="T">Generic class or interface.</typeparam> |
| | 28 | | /// <param name="uri">Uniform Resource Identifier.</param> |
| | 29 | | /// <param name="options">Json serializer options.</param> |
| | 30 | | /// <returns>The deleted resource, or <see langword="null"/> if there were errors while processing the JSON.</returns> |
| | 31 | | /// <exception cref="AggregateException">Represents one or more errors that occur during execution.</exception> |
| | 32 | | public T? DeleteResource<T>( string uri, JsonSerializerOptions? options = null ) where T : class |
| 3 | 33 | | { |
| 3 | 34 | | CheckUri( ref uri ); |
| | 35 | | try |
| 3 | 36 | | { |
| 3 | 37 | | HttpRequestMessage rq = new( HttpMethod.Delete, uri ); |
| 3 | 38 | | string json = GetResponseAsync( rq ).Result; |
| 2 | 39 | | return !string.IsNullOrEmpty( json ) ? |
| 2 | 40 | | JsonHelper.DeserializeJson<T>( ref json, options ) : null; |
| | 41 | | } |
| 3 | 42 | | catch( AggregateException ) { throw; } |
| 2 | 43 | | } |
| | 44 | |
|
| | 45 | | /// <summary>Gets a resource.</summary> |
| | 46 | | /// <param name="uri">Uniform Resource Identifier.</param> |
| | 47 | | /// <returns>Resource as a JSON string if there were no errors while processing the request.</returns> |
| | 48 | | /// <exception cref="AggregateException">Represents one or more errors that occur during execution.</exception> |
| | 49 | | public string? GetResource( string uri ) |
| 18 | 50 | | { |
| 18 | 51 | | CheckUri( ref uri ); |
| | 52 | | try |
| 18 | 53 | | { |
| 18 | 54 | | HttpRequestMessage rq = new( HttpMethod.Get, uri ); |
| 18 | 55 | | return GetResponseAsync( rq ).Result; |
| | 56 | | } |
| 3 | 57 | | catch( AggregateException ) { throw; } |
| 17 | 58 | | } |
| | 59 | |
|
| | 60 | | /// <summary>Creates a resource.</summary> |
| | 61 | | /// <typeparam name="T">Generic class or interface.</typeparam> |
| | 62 | | /// <param name="uri">Uniform Resource Identifier.</param> |
| | 63 | | /// <param name="obj">Resource to create.</param> |
| | 64 | | /// <param name="options">Json serializer options.</param> |
| | 65 | | /// <returns>The created resource, or <see langword="null"/> if there were errors while processing the JSON.</returns> |
| | 66 | | /// <exception cref="AggregateException">Represents one or more errors that occur during execution.</exception> |
| | 67 | | public T? PostResource<T>( string uri, T? obj, JsonSerializerOptions? options = null ) where T : class |
| 4 | 68 | | { |
| 4 | 69 | | string? json = JsonHelper.Serialize( obj ); |
| 6 | 70 | | if( json is null ) { return null; } |
| | 71 | |
|
| 3 | 72 | | CheckUri( ref uri ); |
| | 73 | | try |
| 3 | 74 | | { |
| 3 | 75 | | HttpRequestMessage rq = new( HttpMethod.Post, uri ) |
| 3 | 76 | | { |
| 3 | 77 | | Content = new StringContent( json, Encoding.UTF8, _mediaType ) |
| 3 | 78 | | }; |
| 3 | 79 | | json = GetResponseAsync( rq ).Result; |
| 2 | 80 | | return !string.IsNullOrEmpty( json ) ? |
| 2 | 81 | | JsonHelper.DeserializeJson<T>( ref json, options ) : null; |
| | 82 | | } |
| 3 | 83 | | catch( AggregateException ) { throw; } |
| 3 | 84 | | } |
| | 85 | |
|
| | 86 | | /// <summary>Updates a resource.</summary> |
| | 87 | | /// <typeparam name="T">Generic class or interface.</typeparam> |
| | 88 | | /// <param name="uri">Uniform Resource Identifier.</param> |
| | 89 | | /// <param name="obj">Resource to update.</param> |
| | 90 | | /// <param name="options">Json serializer options.</param> |
| | 91 | | /// <returns>The updated resource, or <see langword="null"/> if there were errors while processing the JSON.</returns> |
| | 92 | | /// <exception cref="AggregateException">Represents one or more errors that occur during execution.</exception> |
| | 93 | | public T? PutResource<T>( string uri, T? obj, JsonSerializerOptions? options = null ) where T : class |
| 4 | 94 | | { |
| 4 | 95 | | string? json = JsonHelper.Serialize( obj, options ); |
| 6 | 96 | | if( json is null ) { return null; } |
| | 97 | |
|
| 3 | 98 | | CheckUri( ref uri ); |
| | 99 | | try |
| 3 | 100 | | { |
| 3 | 101 | | HttpRequestMessage rq = new( HttpMethod.Put, uri ) |
| 3 | 102 | | { |
| 3 | 103 | | Content = new StringContent( json, Encoding.UTF8, _mediaType ) |
| 3 | 104 | | }; |
| 3 | 105 | | json = GetResponseAsync( rq ).Result; |
| 2 | 106 | | return !string.IsNullOrEmpty( json ) ? |
| 2 | 107 | | JsonHelper.DeserializeJson<T>( ref json, options ) : null; |
| | 108 | | } |
| 3 | 109 | | catch( AggregateException ) { throw; } |
| 3 | 110 | | } |
| | 111 | |
|
| | 112 | | #region Private Methods |
| | 113 | |
|
| | 114 | | private void CheckUri( ref string uri ) |
| 27 | 115 | | { |
| 27 | 116 | | if( !uri.StartsWith( Uri.UriSchemeHttp, StringComparison.InvariantCultureIgnoreCase ) ) |
| 26 | 117 | | { |
| 26 | 118 | | uri = _httpClient.BaseAddress?.OriginalString + uri; |
| 26 | 119 | | } |
| 27 | 120 | | } |
| | 121 | |
|
| | 122 | | private static HttpClient CreateSharedClient( string siteUri, int timeout ) |
| 32 | 123 | | { |
| 32 | 124 | | SocketsHttpHandler handler = new() |
| 32 | 125 | | { |
| 32 | 126 | | PooledConnectionLifetime = TimeSpan.FromMinutes( 15 ) // Recreate every 15 minutes |
| 32 | 127 | | }; |
| | 128 | |
|
| 80 | 129 | | if( siteUri.Contains( '\\' ) ) { siteUri = siteUri.Replace( '\\', '/' ); } |
| 128 | 130 | | if( !siteUri.EndsWith( '/' ) ) { siteUri += "/"; } |
| 32 | 131 | | TimeSpan timespan = TimeSpan.FromSeconds( timeout ); |
| | 132 | |
|
| 32 | 133 | | return new HttpClient( handler ) { BaseAddress = new Uri( siteUri ), Timeout = timespan }; |
| 32 | 134 | | } |
| | 135 | |
|
| | 136 | | // https://devblogs.microsoft.com/dotnet/configureawait-faq/ |
| | 137 | | private async Task<string> GetResponseAsync( HttpRequestMessage request ) |
| 27 | 138 | | { |
| 27 | 139 | | HttpResponseMessage response = await _httpClient.SendAsync( request ).ConfigureAwait( false ); |
| 27 | 140 | | _ = response.EnsureSuccessStatusCode(); |
| | 141 | |
|
| 23 | 142 | | using StreamReader reader = new( response.Content.ReadAsStream() ); |
| 23 | 143 | | return await reader.ReadToEndAsync().ConfigureAwait( false ); |
| 23 | 144 | | } |
| | 145 | |
|
| | 146 | | #endregion |
| | 147 | | } |