diff --git a/DragonFruit.Common.Data.Tests/Advanced/Objects/DatabaseUpdateRequest.cs b/DragonFruit.Common.Data.Tests/Advanced/Objects/DatabaseUpdateRequest.cs
index 3f10cc2..046d861 100644
--- a/DragonFruit.Common.Data.Tests/Advanced/Objects/DatabaseUpdateRequest.cs
+++ b/DragonFruit.Common.Data.Tests/Advanced/Objects/DatabaseUpdateRequest.cs
@@ -12,7 +12,7 @@ internal class DatabaseUpdateRequest : ApiRequest
protected override Methods Method => Methods.Post;
- protected override DataTypes DataType => DataTypes.SerializedProperty;
+ protected override BodyType BodyType => BodyType.SerializedProperty;
[RequestBody]
public Employee Employee { get; set; } = new Employee
diff --git a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/Objects/AuthRequest.cs b/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/Objects/AuthRequest.cs
index f63a9c4..4977dd9 100644
--- a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/Objects/AuthRequest.cs
+++ b/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/Objects/AuthRequest.cs
@@ -11,7 +11,7 @@ public class AuthRequest : ApiRequest
{
public override string Path => "https://osu.ppy.sh/oauth/token";
protected override Methods Method => Methods.Post;
- protected override DataTypes DataType => DataTypes.Encoded;
+ protected override BodyType BodyType => BodyType.Encoded;
[FormParameter("grant_type")]
public string Grant => "client_credentials";
diff --git a/DragonFruit.Common.Data/ApiClient.cs b/DragonFruit.Common.Data/ApiClient.cs
index f5f0c89..0b05802 100644
--- a/DragonFruit.Common.Data/ApiClient.cs
+++ b/DragonFruit.Common.Data/ApiClient.cs
@@ -52,14 +52,14 @@ public ApiClient(ISerializer serializer)
public string UserAgent { get; set; }
///
- /// Additional headers to be sent with the requests
+ /// The Authorization header value
///
- public HashableDictionary CustomHeaders { get; set; } = new HashableDictionary();
+ public string Authorization { get; set; }
///
- /// The Authorization header
+ /// Additional headers to be sent with the requests
///
- public string Authorization { get; set; }
+ public HashableDictionary CustomHeaders { get; set; } = new HashableDictionary();
///
/// Optional to be consumed by the
@@ -75,7 +75,7 @@ public ApiClient(ISerializer serializer)
///
/// Defaults to
///
- protected ISerializer Serializer { get; set; }
+ public ISerializer Serializer { get; set; }
///
/// used by these requests. This is used by the library and as such, should **not** be disposed in any way
@@ -87,11 +87,6 @@ public ApiClient(ISerializer serializer)
///
protected virtual int AdjustmentTimeout => 200;
- ///
- /// Last made for using with
- ///
- private ApiRequest CachedRequest { get; set; }
-
#endregion
#region Clients, Hashes and Locks
@@ -136,8 +131,7 @@ protected virtual HttpClient GetClient()
//lock for modification
if (!Monitor.TryEnter(_clientAdjustmentLock, AdjustmentTimeout))
{
- throw new TimeoutException(
- $"The {nameof(ApiClient)} is being overloaded with reconstruction requests. Consider creating a separate {nameof(ApiClient)} and delegating clients to specific types of requests");
+ throw new TimeoutException($"The {nameof(ApiClient)} is being overloaded with reconstruction requests. Consider creating a separate {nameof(ApiClient)} and delegating clients to specific types of requests");
}
//wait for all ongoing requests to end
@@ -238,28 +232,24 @@ protected virtual void SetupRequest(HttpRequestMessage request)
public virtual HttpResponseMessage Perform(ApiRequest requestData)
{
ValidateRequest(requestData);
-
- // perform and return postProcess result
- return InternalPerform(requestData.GetRequest(Serializer), response => response, false);
+ return Perform(requestData.Build(this));
}
///
- /// Perform a pre-fabricated
+ /// Perform an with a specified return type.
///
- public virtual HttpResponseMessage Perform(HttpRequestMessage request)
+ public virtual T Perform(ApiRequest requestData) where T : class
{
- return InternalPerform(request, response => response, false);
+ ValidateRequest(requestData);
+ return Perform(requestData.Build(this));
}
///
- /// Perform an with a specified return type.
+ /// Perform a pre-fabricated
///
- public virtual T Perform(ApiRequest requestData) where T : class
+ public virtual HttpResponseMessage Perform(HttpRequestMessage request)
{
- ValidateRequest(requestData);
- var request = requestData.GetRequest(Serializer);
-
- return InternalPerform(request, response => ValidateAndProcess(response, request), true);
+ return InternalPerform(request, response => response, false);
}
///
@@ -298,14 +288,19 @@ HttpResponseMessage CopyProcess(HttpResponseMessage response)
return response; //we're not using this so return anything...
}
- _ = InternalPerform(requestData.GetRequest(Serializer), CopyProcess, true);
+ _ = InternalPerform(requestData.Build(this), CopyProcess, true);
}
///
/// Internal procedure for performing a web-request
///
+ ///
+ /// While the consumer has the option to prevent disposal of the produced,
+ /// the passed is always disposed at the end of the request.
+ ///
/// The request to perform
/// to process the
+ /// Whether to dispose of the produced after has been invoked.
protected T InternalPerform(HttpRequestMessage request, Func processResult, bool disposeResponse)
{
//get client and request (disposables)
@@ -360,15 +355,14 @@ protected virtual T ValidateAndProcess(HttpResponseMessage response, HttpRequ
/// The client can't be used because there is no auth url.
protected virtual void ValidateRequest(ApiRequest requestData)
{
- //todo is there any benefit to trying to parse the url?
- if (string.IsNullOrWhiteSpace(requestData.Path))
- {
- throw new NullRequestException();
- }
-
- if (requestData.RequireAuth && (!requestData.Headers.IsValueCreated && string.IsNullOrEmpty(Authorization)))
+ // note request path is validated on build
+ if (requestData.RequireAuth && string.IsNullOrEmpty(Authorization))
{
- throw new ClientValidationException("Authorization data expected, but not found");
+ // check if we have a custom headerset in the request
+ if (!requestData.Headers.IsValueCreated || !requestData.Headers.Value.ContainsKey("Authorization"))
+ {
+ throw new ClientValidationException("Authorization header was expected, but not found (in request or client)");
+ }
}
}
}
diff --git a/DragonFruit.Common.Data/ApiRequest.cs b/DragonFruit.Common.Data/ApiRequest.cs
index 5adbed0..710c632 100644
--- a/DragonFruit.Common.Data/ApiRequest.cs
+++ b/DragonFruit.Common.Data/ApiRequest.cs
@@ -29,9 +29,9 @@ public abstract class ApiRequest
protected virtual Methods Method => Methods.Get;
///
- /// The to use (if there is a body to be sent)
+ /// The to use (if there is a body to be sent)
///
- protected virtual DataTypes DataType { get; }
+ protected virtual BodyType BodyType { get; }
///
/// Whether an auth header is required.
@@ -39,7 +39,7 @@ public abstract class ApiRequest
/// This was set to true but no auth header was specified.
/// Automatically suppressed if the property has been initialised.
///
- public virtual bool RequireAuth => false;
+ protected internal virtual bool RequireAuth => false;
///
/// Custom Headers to send with this request. Overrides any custom header set in the with the same name.
@@ -58,10 +58,10 @@ public abstract class ApiRequest
/// Overridable property for configuring a custom body for this request
///
///
- /// Only used when the is equal to
+ /// Only used when the is equal to
///
///
- public virtual HttpContent BodyContent { get; }
+ protected virtual HttpContent BodyContent { get; }
///
/// used for ToString() conversions when collecting attributed members
@@ -106,19 +106,26 @@ internal IEnumerable> GetParameter() where T : I
}
}
- internal object GetSingleParameterObject() where T : Attribute
- {
- var property = GetType().GetProperties()
- .Single(x => Attribute.GetCustomAttribute(x, typeof(T)) is T);
+ internal object GetSingleParameterObject() where T : Attribute =>
+ GetType().GetProperties()
+ .Single(x => Attribute.GetCustomAttribute(x, typeof(T)) is T)
+ .GetValue(this, null);
- return property.GetValue(this, null);
- }
+ public HttpRequestMessage Build(ApiClient client) => Build(client.Serializer);
///
- /// Creates the default , which can then be overriden by
+ /// Creates a for this , which can then be modified manually or overriden by
///
- internal HttpRequestMessage GetRequest(ISerializer serializer)
+ ///
+ /// This validates the and properties, throwing a if it's unsatisfied with the constraints
+ ///
+ public HttpRequestMessage Build(ISerializer serializer)
{
+ if (!Path.StartsWith("http"))
+ {
+ throw new HttpRequestException("The request path is invalid (it must start with http or https)");
+ }
+
var request = new HttpRequestMessage { RequestUri = new Uri(FullUrl) };
//generic setup
@@ -139,7 +146,7 @@ internal HttpRequestMessage GetRequest(ISerializer serializer)
break;
case Methods.Patch:
- request.Method = new HttpMethod("PATCH"); //in .NET standard 2 patch isn't implemented...
+ request.Method = new HttpMethod("PATCH"); //in .NET Standard 2.0 patch isn't implemented...
request.Content = GetContent(serializer);
break;
@@ -152,6 +159,10 @@ internal HttpRequestMessage GetRequest(ISerializer serializer)
request.Method = HttpMethod.Head;
break;
+ case Methods.Trace:
+ request.Method = HttpMethod.Trace;
+ break;
+
default:
throw new NotImplementedException();
}
@@ -171,19 +182,19 @@ internal HttpRequestMessage GetRequest(ISerializer serializer)
private HttpContent GetContent(ISerializer serializer)
{
- switch (DataType)
+ switch (BodyType)
{
- case DataTypes.Encoded:
+ case BodyType.Encoded:
return new FormUrlEncodedContent(GetParameter());
- case DataTypes.Serialized:
+ case BodyType.Serialized:
return serializer.Serialize(this);
- case DataTypes.SerializedProperty:
+ case BodyType.SerializedProperty:
var body = serializer.Serialize(GetSingleParameterObject());
return body;
- case DataTypes.Custom:
+ case BodyType.Custom:
return BodyContent;
default:
@@ -191,7 +202,5 @@ private HttpContent GetContent(ISerializer serializer)
throw new ArgumentOutOfRangeException();
}
}
-
- internal ApiRequest Clone() => (ApiRequest)MemberwiseClone();
}
}
diff --git a/DragonFruit.Common.Data/Methods.cs b/DragonFruit.Common.Data/Methods.cs
index f0ef61f..0eda7f6 100644
--- a/DragonFruit.Common.Data/Methods.cs
+++ b/DragonFruit.Common.Data/Methods.cs
@@ -12,10 +12,11 @@ public enum Methods
Post,
Put,
Patch,
- Delete
+ Delete,
+ Trace
}
- public enum DataTypes
+ public enum BodyType
{
///
/// Finds all properties marked with and creates a url-form encoded content from them