I've a C# WinForms application that relies heavily on sending HttpWebRequest
s. I've build an HttpWebRequestBuilder
, and WebRequestBodyBuilder
.
public class HttpWebRequestBuilder
{
private HttpWebRequest _httpWebRequest; // The object that will be built.
public HttpWebRequestBuilder(string url) : this(url, Constants.FirefoxUserAgent) // Use firefox user-agent by default (Constants is a static class that has FirefoxUserAgent constant).
{
}
public HttpWebRequestBuilder(string url, string userAgent)
{
_httpWebRequest = WebRequest.CreateHttp(url); // Create an HttpWebRequest.
_httpWebRequest.UserAgent = userAgent; // Set the user-agent.
_httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; // Set automatic decompression. I want this value for all requests.
}
public HttpWebRequestBuilder WithMethod(string method) // Set the method, "GET", "POST", etc. The default is "GET".
{
_httpWebRequest.Method = method;
return this;
}
public HttpWebRequestBuilder Accepts(string accept) // Set the "Accept" header.
{
_httpWebRequest.Accept = accept;
return this;
}
public HttpWebRequestBuilder WithContentType(string contentType) // Set the "Content-Type" header.
{
_httpWebRequest.ContentType = contentType;
return this;
}
public HttpWebRequestBuilder WithCookies(CookieContainer container) // Set a cookie container.
{
_httpWebRequest.CookieContainer = container;
return this;
}
public HttpWebRequestBuilder AllowAutoRedirect(bool allow) // Set AllowAutoRedirect
{
_httpWebRequest.AllowAutoRedirect = allow;
return this;
}
public HttpWebRequestBuilder WithReferer(string referer) // Set referer.
{
_httpWebRequest.Referer = referer;
return this;
}
public HttpWebRequestBuilder WithCustomHeader(string name, string value) // Add a custom header.
{
_httpWebRequest.Headers.Add(name, value);
return this;
}
public async Task<HttpWebRequestBuilder> PostAsync(WebRequestBodyBuilder bodyBuilder) // Takes an object of WebRequestBodyBuilder (which just makes a string like "param1=value1¶m2=value2&...."
{
byte[] buffer = Encoding.UTF8.GetBytes(bodyBuilder.ToString());
using (var reqStrm = await _httpWebRequest.GetRequestStreamAsync()) // Write the body in the request stream.
{
reqStrm.Write(buffer, 0, buffer.Length);
}
return this;
}
public HttpWebRequest Build() // Returns the actual HttpWebRequest
{
return _httpWebRequest;
}
}
public class WebRequestBodyBuilder
{
private StringBuilder _stringBuilder = new StringBuilder();
public WebRequestBodyBuilder SetKeyValuePair(string key, string value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentException($"Argument {nameof(key)} can't be null or whitespace."); // key can't be null, but value CAN!
if (!string.IsNullOrWhiteSpace(_stringBuilder.ToString()))
_stringBuilder.Append("&"); // First call shouldn't add "&" in the first.
_stringBuilder.Append($"{WebUtility.UrlEncode(key)}={WebUtility.UrlEncode(value)}");
return this;
}
public override string ToString()
{
return _stringBuilder.ToString(); // ToString is used in the HttpWebRequestBuilder, in the PostAsync method.
}
}
An example usage of these is like the following:
var bodyBuilder = new WebRequestBodyBuilder()
.SetKeyValuePair("username", Account.Username)
.SetKeyValuePair("password", Account.Password)
.SetKeyValuePair("auth_token", _token);
HttpWebRequest req = (await (new HttpWebRequestBuilder() // The await is for PostAsync.
.WithMethod("POST")
.WithReferer("https://website.com/login")
.WithCookies(_cookies)
.Accepts("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.WithContentType("application/x-www-form-urlencoded")
.AllowAutoRedirect(false).PostAsync(bodyBuilder))).Build();
// Do something with req, like await req.GetResponseAsync()
I'm just starting learning design patterns, so I want to make sure that I'm not misusing them.
I'm aware that docs recommends HttpClient over HttpWebRequest, but this is another story. My question is specific about applying the builder pattern in this case