Skip to content

Commit 64093d8

Browse files
New MapRester method added and project code cleanup
1 parent 6a82be4 commit 64093d8

File tree

10 files changed

+134
-87
lines changed

10 files changed

+134
-87
lines changed

Tigernet.Samples.RestApi/Program.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
using Tigernet.Hosting;
22
using Tigernet.Samples.RestApi.Abstractions;
33
using Tigernet.Samples.RestApi.Clevers;
4-
using Tigernet.Samples.RestApi.Resters;
54

65
var builder = new TigernetHostBuilder("http://localhost:5000/");
76

87
builder.AddService<IUserClever, UserClever>();
98

10-
builder.MapRester<UsersRester>();
11-
builder.MapRester<HomeRester>();
9+
builder.MapResters();
1210

1311
//builder.UseAsync(async (context) =>
1412
//{

Tigernet.Samples.RestApi/Resters/HomeRester.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using Tigernet.Hosting.Actions;
22
using Tigernet.Hosting.Attributes.HttpMethods;
3+
using Tigernet.Hosting.Attributes.Resters;
34

45
namespace Tigernet.Samples.RestApi.Resters
56
{
7+
[ApiRester]
68
public class HomeRester : ResterBase
79
{
810
[Getter]

Tigernet.Samples.RestApi/Resters/UsersRester.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using Tigernet.Hosting.Actions;
22
using Tigernet.Hosting.Attributes.HttpMethods;
3+
using Tigernet.Hosting.Attributes.Resters;
34
using Tigernet.Samples.RestApi.Abstractions;
45
using Tigernet.Samples.RestApi.Models;
56
namespace Tigernet.Samples.RestApi.Resters
67
{
8+
[ApiRester]
79
public class UsersRester : ResterBase
810
{
911
private readonly IUserClever userClever;
@@ -35,7 +37,7 @@ public object Add()
3537
Name = "Ikrom",
3638
Age = 28
3739

38-
};
40+
};
3941

4042
return Ok(userClever.Add(user));
4143
}

src/Tigernet.Hosting/Attributes/HttpMethods/Commons/HttpMethodAttribute.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// Identifies an action that supports a given set of HTTP methods.
55
/// </summary>
66
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
7-
public abstract class HttpMethodAttribute : Attribute
7+
public abstract class HttpMethodAttribute : Attribute
88
{
99
/// <summary>
1010
/// The <see href="route"/> field represents the route of the HTTP method decorated by the attribute.
@@ -21,7 +21,7 @@ public abstract class HttpMethodAttribute : Attribute
2121
/// </summary>
2222
/// <param name="route">path of route</param>
2323
public HttpMethodAttribute(string route = null)
24-
{
25-
this.route = route;
26-
}
24+
{
25+
this.route = route;
26+
}
2727
}

src/Tigernet.Hosting/Attributes/HttpMethods/GetterAttribute.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ namespace Tigernet.Hosting.Attributes.HttpMethods;
77
/// </summary>
88
public class GetterAttribute : HttpMethodAttribute
99
{
10-
/// <inheritdoc />
11-
public GetterAttribute(string route = null)
12-
: base(route)
13-
{
14-
}
15-
16-
/// <inheritdoc/>
10+
/// <inheritdoc />
11+
public GetterAttribute(string route = null)
12+
: base(route)
13+
{
14+
}
15+
16+
/// <inheritdoc/>
1717
internal override string HttpMethodName { get => "GET"; }
1818
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Tigernet.Hosting.Attributes.Resters
2+
{
3+
public class ApiResterAttribute : Attribute
4+
{
5+
}
6+
}

src/Tigernet.Hosting/Exceptions/ArgumentNullException.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ namespace Tigernet.Hosting.Exceptions;
33
public class ArgumentNullException : Exception
44
{
55
public ArgumentNullException(string argumentName)
6-
:base( $"The argument '{argumentName}' cannot be null, please provide a valid input!")
6+
: base($"The argument '{argumentName}' cannot be null, please provide a valid input!")
77
{
88
}
9-
9+
1010
}

src/Tigernet.Hosting/Exceptions/RouteNotFoundException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace Tigernet.Hosting.Exceptions;
33
public class RouteNotFoundException : Exception
44
{
55
public RouteNotFoundException(string route, string message)
6-
:base($"The route '{route}' could not be found, please check the route details and provide a valid route!")
6+
: base($"The route '{route}' could not be found, please check the route details and provide a valid route!")
77
{
88
}
99
}

src/Tigernet.Hosting/TigernetHostBuilder.DI.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Concurrent;
2-
namespace Tigernet.Hosting
1+
namespace Tigernet.Hosting
32
{
43
public partial class TigernetHostBuilder
54
{

src/Tigernet.Hosting/TigernetHostBuilder.cs

+107-67
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System.Collections.Concurrent;
1+
using System.Data;
22
using System.Net;
3-
using System.Net;
3+
using System.Reflection;
44
using System.Text;
5-
using Tigernet.Hosting.Actions;
6-
using Tigernet.Hosting.Attributes;
75
using Tigernet.Hosting.Attributes.Commons;
6+
using Tigernet.Hosting.Attributes.HttpMethods;
7+
using Tigernet.Hosting.Attributes.Resters;
88
using Tigernet.Hosting.Exceptions;
99

1010
namespace Tigernet.Hosting
@@ -125,86 +125,126 @@ private async Task HandleRequestAsync(HttpListenerContext context)
125125
response.Close();
126126
}
127127
}
128-
/// <summary>
129-
/// Maps the REST API endpoint for the given route and ResterBase implementation.
130-
/// The methods decorated with the GetterAttribute are extracted and mapped to their corresponding route URL
131-
/// The response is returned in JSON format.
132-
/// </summary>
133-
/// <typeparam name="T">The type of the ResterBase implementations</typeparam>
134-
/// <param name="route">The base route URL for the REST API endpoints</param>
135-
public void MapRester<T>(string route = null) where T : ResterBase
128+
129+
/// <summary>
130+
/// Using middleware
131+
/// </summary>
132+
/// <param name="middleware"></param>
133+
/// <returns></returns>
134+
public TigernetHostBuilder UseAsync(Func<HttpListenerContext, Task> middleware)
135+
{
136+
_middlewares.Add(middleware);
137+
138+
return this;
139+
}
140+
141+
public void MapResters()
142+
{
143+
// get the assembly that is using this library
144+
var assembly = Assembly.GetCallingAssembly();
145+
146+
// get all types in the assembly
147+
var types = assembly.GetTypes();
148+
149+
// filter for types that have the ApiRester attribute
150+
var resterTypes = types.Where(t => t.GetCustomAttribute<ApiResterAttribute>() != null);
151+
152+
foreach (var resterType in resterTypes)
136153
{
137-
T rester;
138-
var type = typeof(T);
139-
var constructor = type.GetConstructors()[0];
140-
var parameters = constructor.GetParameters();
141-
if (parameters.Length == 0)
154+
var methods = resterType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
155+
.Where(m => m.GetCustomAttribute<GetterAttribute>() != null || m.GetCustomAttribute<PosterAttribute>() != null);
156+
157+
var typeName = resterType.Name;
158+
159+
foreach (var method in methods)
142160
{
143-
rester = (T)Activator.CreateInstance(type);
161+
var getterAttr = method.GetCustomAttribute<GetterAttribute>();
162+
var posterAttr = method.GetCustomAttribute<PosterAttribute>();
163+
164+
var endpointAttr = getterAttr != null ? getterAttr : (HttpMethodAttribute)posterAttr;
165+
166+
var route = Path.Combine("/", typeName.Split(new[] { "Rester" },
167+
StringSplitOptions.None).FirstOrDefault());
168+
169+
var routeUrl = (route + endpointAttr.route).ToLower();
170+
171+
var handler = CreateHandlerFunc(resterType, method);
172+
173+
MapRoute(routeUrl, handler);
144174
}
175+
}
176+
}
145177

146-
else
178+
private Func<HttpListenerContext, Task> CreateHandlerFunc(Type resterType, MethodInfo method)
179+
{
180+
return async context =>
181+
{
182+
object rester;
183+
var constructor = resterType.GetConstructors().FirstOrDefault();
184+
185+
if (constructor != null)
147186
{
187+
var parameters = constructor.GetParameters();
148188
var parameterInstances = new object[parameters.Length];
189+
149190
for (var i = 0; i < parameters.Length; i++)
150191
{
151-
parameterInstances[i] = GetService(parameters[i].ParameterType);
192+
var parameterType = parameters[i].ParameterType;
193+
var service = GetService(parameterType);
194+
if (service != null)
195+
{
196+
parameterInstances[i] = service;
197+
}
198+
else
199+
{
200+
throw new Exception($"Unable to resolve service of type {parameterType} for constructor of {resterType}.");
201+
}
152202
}
153203

154-
rester = (T)constructor.Invoke(parameterInstances);
204+
rester = constructor.Invoke(parameterInstances);
155205
}
156-
var typeName = type.Name;
157-
var methods = type.GetMethods();
158-
foreach (var method in methods)
206+
else
159207
{
160-
var attributes = method.GetCustomAttributes(typeof(HttpMethodAttribute), false);
161-
if (attributes.Length > 0)
162-
{
163-
var attribute = attributes[0] as HttpMethodAttribute;
208+
rester = Activator.CreateInstance(resterType);
209+
}
164210

165-
// if route is null, use the route from the class name
166-
if (string.IsNullOrEmpty(route))
167-
{
168-
route = Path.Combine("/", typeName.Split(new[] { "Rester" },
169-
StringSplitOptions.None).FirstOrDefault());
170-
}
211+
var args = GetArguments(method, context);
212+
var result = method.Invoke(rester, args);
171213

172-
var routeUrl = (route + attribute.route).ToLower();
173-
MapRoute(routeUrl, async context =>
174-
{
175-
var response = context.Response;
176-
response.ContentType = "application/json";
177-
178-
if (context.Request.HttpMethod == attribute.HttpMethodName)
179-
{
180-
var result = method.Invoke(rester, null);
181-
var content = Encoding.UTF8.GetBytes(result.ToString());
182-
response.ContentLength64 = content.Length;
183-
using (var output = response.OutputStream)
184-
{
185-
await output.WriteAsync(content, 0, content.Length);
186-
}
187-
}
188-
else
189-
{
190-
response.StatusCode = (int)HttpStatusCode.NotFound;
191-
response.Close();
192-
}
193-
});
194-
}
214+
if (result is Task task)
215+
{
216+
await task;
195217
}
196-
}
197218

198-
/// <summary>
199-
/// Using middleware
200-
/// </summary>
201-
/// <param name="middleware"></param>
202-
/// <returns></returns>
203-
public TigernetHostBuilder UseAsync(Func<HttpListenerContext, Task> middleware)
204-
{
205-
_middlewares.Add(middleware);
219+
var response = context.Response;
220+
var content = Encoding.UTF8.GetBytes(result.ToString());
221+
response.ContentLength64 = content.Length;
222+
using (var output = response.OutputStream)
223+
{
224+
await output.WriteAsync(content, 0, content.Length);
225+
}
226+
};
227+
}
206228

207-
return this;
229+
private object[] GetArguments(MethodInfo method, HttpListenerContext context)
230+
{
231+
var parameters = method.GetParameters();
232+
var args = new object[parameters.Length];
233+
234+
for (int i = 0; i < parameters.Length; i++)
235+
{
236+
var parameterType = parameters[i].ParameterType;
237+
if (parameterType == typeof(HttpListenerContext))
238+
{
239+
args[i] = context;
240+
}
241+
else
242+
{
243+
args[i] = null;
244+
}
208245
}
246+
247+
return args;
209248
}
249+
}
210250
}

0 commit comments

Comments
 (0)