Multi-Tier Integration Service

Created: C# Service for integrating multi-tier applications

Updated: 03 September 2023

For applications that span multiple tiers or make use of a separate data accesss and web application layers a generic integration service can be used to communicate between tiers

Service Definition

The service enables you to call controllers on the underlying tier by their route, provided that complex objects are supplied in the request body where needed

1
public class IntegrationService
2
{
3
private string _apiBaseUrl;
4
private WebProxy _proxy;
5
6
public IntegrationService(string apiBaseUrl, WebProxy proxy = null)
7
{
8
_apiBaseUrl = apiBaseUrl;
9
_proxy = proxy;
10
}
11
12
/// <summary>
13
/// Creates a new HttpClient instance that can be used to make HTTP requests to the API endpoint with the endpoint and proxy precofigured
14
/// Supports basic HTTP methods and configurations. Essentially enables client to call a function on another application's route with
15
/// params as a single POST DTO if necessary
16
/// </summary>
17
/// <returns></returns>
18
public HttpClient GetClient()
19
{
20
HttpClient client;
21
22
if (_proxy != null)
23
{
24
var handler = new HttpClientHandler()
25
{
26
Proxy = _proxy
27
};
28
client = new HttpClient(handler);
29
}
30
else
31
{
32
client = new HttpClient();
33
}
34
35
client.BaseAddress = new Uri(_apiBaseUrl);
36
37
return client;
38
}
39
40
#region GET Methods
41
public async Task<string> Get(string path)
42
{
43
using (var client = GetClient())
44
{
45
var response = await client.GetStringAsync(path);
46
return response;
47
}
48
}
49
50
public async Task<T> Get<T>(string path)
51
{
52
using (var client = GetClient())
53
{
54
var response = await client.GetStringAsync(path);
55
var data = JsonConvert.DeserializeObject<T>(response);
56
return data;
57
}
58
}
59
#endregion
60
61
#region POST Methods
62
public async Task<string> Post(string path, object requestData)
63
{
64
using (var client = GetClient())
65
{
66
var requestContent = new StringContent(
67
JsonConvert.SerializeObject(requestData),
68
Encoding.UTF8,
69
"application/json"
70
);
71
72
var response = await client.PostAsync(path, requestContent);
73
var responseString = await response.Content.ReadAsStringAsync();
74
75
return responseString;
76
}
77
}
78
79
public async Task<T> Post<T>(string path, object requestData)
80
{
81
using (var client = GetClient())
82
{
83
var requestContent = new StringContent(
84
JsonConvert.SerializeObject(requestData),
85
Encoding.UTF8,
86
"application/json"
87
);
88
89
var response = await client.PostAsync(path, requestContent);
90
var responseString = await response.Content.ReadAsStringAsync();
91
var data = JsonConvert.DeserializeObject<T>(responseString);
92
93
return data;
94
}
95
}
96
#endregion
97
98
#region PUT Methods
99
public async Task<string> Put(string path, object requestData)
100
{
101
using (var client = GetClient())
102
{
103
var requestContent = new StringContent(
104
JsonConvert.SerializeObject(requestData),
105
Encoding.UTF8,
106
"application/json"
107
);
108
109
var response = await client.PutAsync(path, requestContent);
110
var responseString = await response.Content.ReadAsStringAsync();
111
112
return responseString;
113
}
114
}
115
116
public async Task<T> Put<T>(string path, object requestData)
117
{
118
using (var client = GetClient())
119
{
120
var requestContent = new StringContent(
121
JsonConvert.SerializeObject(requestData),
122
Encoding.UTF8,
123
"application/json"
124
);
125
126
var response = await client.PutAsync(path, requestContent);
127
var responseString = await response.Content.ReadAsStringAsync();
128
var data = JsonConvert.DeserializeObject<T>(responseString);
129
130
return data;
131
}
132
}
133
#endregion
134
135
#region DELETE Methods
136
public async Task<string> Delete(string path)
137
{
138
using (var client = GetClient())
139
{
140
var response = await client.DeleteAsync(path);
141
var responseString = await response.Content.ReadAsStringAsync();
142
143
return responseString;
144
}
145
}
146
147
public async Task<T> Delete<T>(string path)
148
{
149
using (var client = GetClient())
150
{
151
var response = await client.DeleteAsync(path);
152
var responseString = await response.Content.ReadAsStringAsync();
153
var data = JsonConvert.DeserializeObject<T>(responseString);
154
155
return data;
156
}
157
}
158
#endregion
159
}
160
161
/// <summary>
162
/// Proxy settings, needed for accessing accross tiers
163
/// </summary>
164
public class ProxySettings
165
{
166
public string Endpoint { get; set; }
167
public bool BypassOnLocal { get; set; } = true;
168
public bool EnvironmentHasProxy() => !string.IsNullOrEmpty(Endpoint);
169
}

Confugure the Service

You can configure the service in your startup.cs as follows:

1
services.AddScoped<IntegrationService>(s =>
2
{
3
var proxySettings = Configuration
4
.GetSection("ProxySettings")
5
.Get<ProxySettings>();
6
7
var intBaseUrl = Configuration.GetValue<string>("IntegrationUrl");
8
9
if (proxySettings.EnvironmentHasProxy())
10
{
11
var proxy = new WebProxy(
12
proxySettings.Endpoint,
13
proxySettings.BypassOnLocal
14
);
15
16
return new IntegrationService(intBaseUrl, proxy);
17
}
18
else
19
{
20
return new IntegrationService(intBaseUrl);
21
}
22
});

The above is a bit overkill as it does the proxy checking too, but you don’t need that if you know your proxy configuration beforehand or are not using a proxy

Using the Service

To use the service you need to request it in a controller constructor via Dependency Injection

1
public class MyController : Controller {
2
3
private IntegrationService _integrationService;
4
5
public MyController(IntegrationService integrationService)
6
{
7
_integrationService = integrationService;
8
}
9
10
public async task<MyObject> MyFunction(ComplexObject data)
11
{
12
var result = await integrationService.Post<MyObject>("api/DoStuff", data);
13
14
return result;
15
}
16
}

You will need the following in your appsettings.json

1
...,
2
"ProxySettings: {
3
"Endpoint": "",
4
"BypassOnLocal": true
5
},
6
...