Skip to content

Commit 97373c0

Browse files
committed
+ delegating implementations for easier extensibility.
1 parent 737caa2 commit 97373c0

15 files changed

+921
-5
lines changed

Client/Client.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<Compile Include="Default\FluentClient.cs" />
4646
<Compile Include="Default\RequestBuilder.cs" />
4747
<Compile Include="Default\Response.cs" />
48+
<Compile Include="Delegating\DelegatingFluentClient.cs" />
49+
<Compile Include="Delegating\DelegatingRequestBuilder.cs" />
50+
<Compile Include="Delegating\DelegatingResponse.cs" />
4851
<Compile Include="IClient.cs" />
4952
<Compile Include="IRequestBuilder.cs" />
5053
<Compile Include="IResponse.cs" />
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System.Net.Http;
2+
using System.Net.Http.Formatting;
3+
4+
namespace Pathoschild.Http.Client.Delegating
5+
{
6+
/// <summary>Sends HTTP requests and receives responses from a resource identified by a URI. This implementation delegates work to an inner client.</summary>
7+
/// <typeparam name="TMessageHandler">The HTTP message handler type.</typeparam>
8+
public abstract class DelegatingFluentClient<TMessageHandler> : IClient<TMessageHandler>
9+
where TMessageHandler : HttpMessageHandler
10+
{
11+
/*********
12+
** Properties
13+
*********/
14+
/// <summary>The wrapped client implementation.</summary>
15+
protected IClient<TMessageHandler> Implementation { get; set; }
16+
17+
18+
/*********
19+
** Accessors
20+
*********/
21+
/// <summary>The underlying HTTP client.</summary>
22+
public virtual HttpClient BaseClient
23+
{
24+
get { return this.Implementation.BaseClient; }
25+
}
26+
27+
/// <summary>The underlying HTTP message handler.</summary>
28+
public virtual TMessageHandler MessageHandler
29+
{
30+
get { return this.Implementation.MessageHandler; }
31+
}
32+
33+
/// <summary>The formatters used for serializing and deserializing message bodies.</summary>
34+
public virtual MediaTypeFormatterCollection Formatters
35+
{
36+
get { return this.Implementation.Formatters; }
37+
}
38+
39+
40+
/*********
41+
** Public methods
42+
*********/
43+
/// <summary>Create an asynchronous HTTP DELETE request message (but don't dispatch it yet).</summary>
44+
/// <param name="resource">The URI to send the request to.</param>
45+
/// <returns>Returns a request builder.</returns>
46+
public virtual IRequestBuilder Delete(string resource)
47+
{
48+
return this.Implementation.Delete(resource);
49+
}
50+
51+
/// <summary>Create an asynchronous HTTP GET request message (but don't dispatch it yet).</summary>
52+
/// <param name="resource">The URI to send the request to.</param>
53+
/// <returns>Returns a request builder.</returns>
54+
public virtual IRequestBuilder Get(string resource)
55+
{
56+
return this.Implementation.Get(resource);
57+
}
58+
59+
/// <summary>Create an asynchronous HTTP POST request message (but don't dispatch it yet).</summary>
60+
/// <param name="resource">The URI to send the request to.</param>
61+
/// <returns>Returns a request builder.</returns>
62+
public virtual IRequestBuilder Post(string resource)
63+
{
64+
return this.Implementation.Post(resource);
65+
}
66+
67+
/// <summary>Create an asynchronous HTTP POST request message (but don't dispatch it yet).</summary>
68+
/// <typeparam name="TBody">The request body type.</typeparam>
69+
/// <param name="resource">The URI to send the request to.</param>
70+
/// <param name="body">The request body.</param>
71+
/// <returns>Returns a request builder.</returns>
72+
public virtual IRequestBuilder Post<TBody>(string resource, TBody body)
73+
{
74+
return this.Implementation.Post<TBody>(resource, body);
75+
}
76+
77+
/// <summary>Create an asynchronous HTTP PUT request message (but don't dispatch it yet).</summary>
78+
/// <param name="resource">The URI to send the request to.</param>
79+
/// <returns>Returns a request builder.</returns>
80+
public virtual IRequestBuilder Put(string resource)
81+
{
82+
return this.Implementation.Put(resource);
83+
}
84+
85+
/// <summary>Create an asynchronous HTTP PUT request message (but don't dispatch it yet).</summary>
86+
/// <typeparam name="TBody">The request body type.</typeparam>
87+
/// <param name="resource">The URI to send the request to.</param>
88+
/// <param name="body">The request body.</param>
89+
/// <returns>Returns a request builder.</returns>
90+
public virtual IRequestBuilder Put<TBody>(string resource, TBody body)
91+
{
92+
return this.Implementation.Put<TBody>(resource, body);
93+
}
94+
95+
/// <summary>Create an asynchronous request message (but don't dispatch it yet).</summary>
96+
/// <param name="method">The HTTP method.</param>
97+
/// <param name="resource">The URI to send the request to.</param>
98+
/// <returns>Returns a request builder.</returns>
99+
public virtual IRequestBuilder Send(HttpMethod method, string resource)
100+
{
101+
return this.Implementation.Send(method, resource);
102+
}
103+
104+
/// <summary>Create an asynchronous request message (but don't dispatch it yet).</summary>
105+
/// <param name="message">The HTTP request message to send.</param>
106+
/// <returns>Returns a request builder.</returns>
107+
/// <remarks>This is the base method which executes every request.</remarks>
108+
public virtual IRequestBuilder Send(HttpRequestMessage message)
109+
{
110+
return this.Implementation.Send(message);
111+
}
112+
113+
114+
/*********
115+
** Protected methods
116+
*********/
117+
/// <summary>Construct an instance.</summary>
118+
/// <param name="client">The wrapped client implementation.</param>
119+
protected DelegatingFluentClient(IClient<TMessageHandler> client)
120+
{
121+
this.Implementation = client;
122+
}
123+
}
124+
125+
/// <summary>Sends HTTP requests and receives responses from a resource identified by a URI.</summary>
126+
public abstract class DelegatingFluentClient : DelegatingFluentClient<HttpClientHandler>, IClient
127+
{
128+
/// <summary>Construct an instance.</summary>
129+
/// <param name="client">The wrapped client implementation.</param>
130+
protected DelegatingFluentClient(IClient client)
131+
: base(client) { }
132+
}
133+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Http;
5+
using System.Net.Http.Formatting;
6+
using System.Net.Http.Headers;
7+
8+
namespace Pathoschild.Http.Client.Delegating
9+
{
10+
/// <summary>Builds an asynchronous HTTP request. This implementation delegates work to an inner request builder.</summary>
11+
public abstract class DelegatingRequestBuilder : IRequestBuilder
12+
{
13+
/*********
14+
** Properties
15+
*********/
16+
/// <summary>The wrapped request builder implementation.</summary>
17+
protected IRequestBuilder Implementation { get; set; }
18+
19+
20+
/*********
21+
** Accessors
22+
*********/
23+
/// <summary>The underlying HTTP request message.</summary>
24+
public virtual HttpRequestMessage Message
25+
{
26+
get { return this.Implementation.Message; }
27+
set { this.Implementation.Message = value; }
28+
}
29+
30+
/// <summary>The formatters used for serializing and deserializing message bodies.</summary>
31+
public virtual MediaTypeFormatterCollection Formatters
32+
{
33+
get { return this.Implementation.Formatters; }
34+
set { this.Implementation.Formatters = value; }
35+
}
36+
37+
38+
/*********
39+
** Public methods
40+
*********/
41+
/// <summary>Set the body content of the HTTP request.</summary>
42+
/// <param name="body">The value to serialize into the HTTP body content.</param>
43+
/// <param name="contentType">The request body format (or <c>null</c> to use the first supported Content-Type in the <see cref="IRequestBuilder.Formatters"/>).</param>
44+
/// <returns>Returns the request builder for chaining.</returns>
45+
/// <exception cref="InvalidOperationException">No MediaTypeFormatters are available on the API client for this content type.</exception>
46+
public virtual IRequestBuilder WithBody<T>(T body, MediaTypeHeaderValue contentType = null)
47+
{
48+
this.Implementation.WithBody<T>(body, contentType);
49+
return this;
50+
}
51+
52+
/// <summary>Set the body content of the HTTP request.</summary>
53+
/// <param name="body">The value to serialize into the HTTP body content.</param>
54+
/// <param name="formatter">The media type formatter with which to format the request body format.</param>
55+
/// <param name="mediaType">The HTTP media type (or <c>null</c> for the <paramref name="formatter"/>'s default).</param>
56+
/// <returns>Returns the request builder for chaining.</returns>
57+
public virtual IRequestBuilder WithBody<T>(T body, MediaTypeFormatter formatter, string mediaType = null)
58+
{
59+
this.Implementation.WithBody<T>(body, formatter, mediaType);
60+
return this;
61+
}
62+
63+
/// <summary>Set the body content of the HTTP request.</summary>
64+
/// <param name="body">The formatted HTTP body content.</param>
65+
/// <returns>Returns the request builder for chaining.</returns>
66+
public virtual IRequestBuilder WithBodyContent(HttpContent body)
67+
{
68+
this.Implementation.WithBodyContent(body);
69+
return this;
70+
}
71+
72+
/// <summary>Set an HTTP header.</summary>
73+
/// <param name="key">The key of the HTTP header.</param>
74+
/// <param name="value">The value of the HTTP header.</param>
75+
/// <returns>Returns the request builder for chaining.</returns>
76+
public virtual IRequestBuilder WithHeader(string key, string value)
77+
{
78+
this.Implementation.WithHeader(key, value);
79+
return this;
80+
}
81+
82+
/// <summary>Set an HTTP query string argument.</summary>
83+
/// <param name="key">The key of the query argument.</param>
84+
/// <param name="value">The value of the query argument.</param>
85+
/// <returns>Returns the request builder for chaining.</returns>
86+
public virtual IRequestBuilder WithArgument(string key, object value)
87+
{
88+
this.Implementation.WithArgument(key, value);
89+
return this;
90+
}
91+
92+
/// <summary>Customize the underlying HTTP request message.</summary>
93+
/// <param name="request">The HTTP request message.</param>
94+
/// <returns>Returns the request builder for chaining.</returns>
95+
public virtual IRequestBuilder WithCustom(Action<HttpRequestMessage> request)
96+
{
97+
this.Implementation.WithCustom(request);
98+
return this;
99+
}
100+
101+
/// <summary>Asynchronously dispatch the request.</summary>
102+
/// <param name="throwError">Whether to handle errors from the upstream server by throwing an exception.</param>
103+
/// <returns>Returns a response.</returns>
104+
public virtual IResponse Retrieve(bool throwError = true)
105+
{
106+
return this.Implementation.Retrieve(throwError);
107+
}
108+
109+
/// <summary>Dispatch the request and retrieve the response as a deserialized model.</summary>
110+
/// <typeparam name="TResponse">The response body type.</typeparam>
111+
/// <param name="throwError">Whether to handle errors from the upstream server by throwing an exception.</param>
112+
/// <returns>Returns a deserialized model.</returns>
113+
/// <exception cref="ApiException">The HTTP response returned a non-success <see cref="HttpStatusCode"/>, and <paramref name="throwError"/> is <c>true</c>.</exception>
114+
public virtual TResponse RetrieveAs<TResponse>(bool throwError = true)
115+
{
116+
return this.Implementation.RetrieveAs<TResponse>(throwError);
117+
}
118+
119+
/// <summary>Dispatch the request and retrieve the response as a deserialized list of models.</summary>
120+
/// <typeparam name="TResponse">The response body type.</typeparam>
121+
/// <param name="throwError">Whether to handle errors from the upstream server by throwing an exception.</param>
122+
/// <returns>Returns a deserialized list of models.</returns>
123+
/// <exception cref="ApiException">The HTTP response returned a non-success <see cref="HttpStatusCode"/>, and <paramref name="throwError"/> is <c>true</c>.</exception>
124+
public virtual List<TResponse> RetrieveAsList<TResponse>(bool throwError = true)
125+
{
126+
return this.Implementation.RetrieveAsList<TResponse>(throwError);
127+
}
128+
129+
130+
/*********
131+
** Protected methods
132+
*********/
133+
/// <summary>Construct an instance.</summary>
134+
/// <param name="requestBuilder">The wrapped request builder implementation.</param>
135+
protected DelegatingRequestBuilder(IRequestBuilder requestBuilder)
136+
{
137+
this.Implementation = requestBuilder;
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)