| | | 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 | | } |