Skip to content

Commit

Permalink
Merge pull request #17 from tigernetframework/SohibJaynarov
Browse files Browse the repository at this point in the history
New MapRester method added and project code cleanup
  • Loading branch information
dotnetgoo committed Apr 1, 2023
2 parents 6a82be4 + 64093d8 commit b54328d
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 87 deletions.
4 changes: 1 addition & 3 deletions Tigernet.Samples.RestApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using Tigernet.Hosting;
using Tigernet.Samples.RestApi.Abstractions;
using Tigernet.Samples.RestApi.Clevers;
using Tigernet.Samples.RestApi.Resters;

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

builder.AddService<IUserClever, UserClever>();

builder.MapRester<UsersRester>();
builder.MapRester<HomeRester>();
builder.MapResters();

//builder.UseAsync(async (context) =>
//{
Expand Down
2 changes: 2 additions & 0 deletions Tigernet.Samples.RestApi/Resters/HomeRester.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Tigernet.Hosting.Actions;
using Tigernet.Hosting.Attributes.HttpMethods;
using Tigernet.Hosting.Attributes.Resters;

namespace Tigernet.Samples.RestApi.Resters
{
[ApiRester]
public class HomeRester : ResterBase
{
[Getter]
Expand Down
4 changes: 3 additions & 1 deletion Tigernet.Samples.RestApi/Resters/UsersRester.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Tigernet.Hosting.Actions;
using Tigernet.Hosting.Attributes.HttpMethods;
using Tigernet.Hosting.Attributes.Resters;
using Tigernet.Samples.RestApi.Abstractions;
using Tigernet.Samples.RestApi.Models;
namespace Tigernet.Samples.RestApi.Resters
{
[ApiRester]
public class UsersRester : ResterBase
{
private readonly IUserClever userClever;
Expand Down Expand Up @@ -35,7 +37,7 @@ public object Add()
Name = "Ikrom",
Age = 28

};
};

return Ok(userClever.Add(user));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Identifies an action that supports a given set of HTTP methods.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class HttpMethodAttribute : Attribute
public abstract class HttpMethodAttribute : Attribute
{
/// <summary>
/// The <see href="route"/> field represents the route of the HTTP method decorated by the attribute.
Expand All @@ -21,7 +21,7 @@ public abstract class HttpMethodAttribute : Attribute
/// </summary>
/// <param name="route">path of route</param>
public HttpMethodAttribute(string route = null)
{
this.route = route;
}
{
this.route = route;
}
}
14 changes: 7 additions & 7 deletions src/Tigernet.Hosting/Attributes/HttpMethods/GetterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ namespace Tigernet.Hosting.Attributes.HttpMethods;
/// </summary>
public class GetterAttribute : HttpMethodAttribute
{
/// <inheritdoc />
public GetterAttribute(string route = null)
: base(route)
{
}
/// <inheritdoc/>
/// <inheritdoc />
public GetterAttribute(string route = null)
: base(route)
{
}

/// <inheritdoc/>
internal override string HttpMethodName { get => "GET"; }
}
6 changes: 6 additions & 0 deletions src/Tigernet.Hosting/Attributes/Resters/ApiRester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Tigernet.Hosting.Attributes.Resters
{
public class ApiResterAttribute : Attribute
{
}
}
4 changes: 2 additions & 2 deletions src/Tigernet.Hosting/Exceptions/ArgumentNullException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ namespace Tigernet.Hosting.Exceptions;
public class ArgumentNullException : Exception
{
public ArgumentNullException(string argumentName)
:base( $"The argument '{argumentName}' cannot be null, please provide a valid input!")
: base($"The argument '{argumentName}' cannot be null, please provide a valid input!")
{
}

}
2 changes: 1 addition & 1 deletion src/Tigernet.Hosting/Exceptions/RouteNotFoundException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace Tigernet.Hosting.Exceptions;
public class RouteNotFoundException : Exception
{
public RouteNotFoundException(string route, string message)
:base($"The route '{route}' could not be found, please check the route details and provide a valid route!")
: base($"The route '{route}' could not be found, please check the route details and provide a valid route!")
{
}
}
3 changes: 1 addition & 2 deletions src/Tigernet.Hosting/TigernetHostBuilder.DI.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Concurrent;
namespace Tigernet.Hosting
namespace Tigernet.Hosting
{
public partial class TigernetHostBuilder
{
Expand Down
174 changes: 107 additions & 67 deletions src/Tigernet.Hosting/TigernetHostBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Collections.Concurrent;
using System.Data;
using System.Net;
using System.Net;
using System.Reflection;
using System.Text;
using Tigernet.Hosting.Actions;
using Tigernet.Hosting.Attributes;
using Tigernet.Hosting.Attributes.Commons;
using Tigernet.Hosting.Attributes.HttpMethods;
using Tigernet.Hosting.Attributes.Resters;
using Tigernet.Hosting.Exceptions;

namespace Tigernet.Hosting
Expand Down Expand Up @@ -125,86 +125,126 @@ private async Task HandleRequestAsync(HttpListenerContext context)
response.Close();
}
}
/// <summary>
/// Maps the REST API endpoint for the given route and ResterBase implementation.
/// The methods decorated with the GetterAttribute are extracted and mapped to their corresponding route URL
/// The response is returned in JSON format.
/// </summary>
/// <typeparam name="T">The type of the ResterBase implementations</typeparam>
/// <param name="route">The base route URL for the REST API endpoints</param>
public void MapRester<T>(string route = null) where T : ResterBase

/// <summary>
/// Using middleware
/// </summary>
/// <param name="middleware"></param>
/// <returns></returns>
public TigernetHostBuilder UseAsync(Func<HttpListenerContext, Task> middleware)
{
_middlewares.Add(middleware);

return this;
}

public void MapResters()
{
// get the assembly that is using this library
var assembly = Assembly.GetCallingAssembly();

// get all types in the assembly
var types = assembly.GetTypes();

// filter for types that have the ApiRester attribute
var resterTypes = types.Where(t => t.GetCustomAttribute<ApiResterAttribute>() != null);

foreach (var resterType in resterTypes)
{
T rester;
var type = typeof(T);
var constructor = type.GetConstructors()[0];
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
var methods = resterType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.GetCustomAttribute<GetterAttribute>() != null || m.GetCustomAttribute<PosterAttribute>() != null);

var typeName = resterType.Name;

foreach (var method in methods)
{
rester = (T)Activator.CreateInstance(type);
var getterAttr = method.GetCustomAttribute<GetterAttribute>();
var posterAttr = method.GetCustomAttribute<PosterAttribute>();

var endpointAttr = getterAttr != null ? getterAttr : (HttpMethodAttribute)posterAttr;

var route = Path.Combine("/", typeName.Split(new[] { "Rester" },
StringSplitOptions.None).FirstOrDefault());

var routeUrl = (route + endpointAttr.route).ToLower();

var handler = CreateHandlerFunc(resterType, method);

MapRoute(routeUrl, handler);
}
}
}

else
private Func<HttpListenerContext, Task> CreateHandlerFunc(Type resterType, MethodInfo method)
{
return async context =>
{
object rester;
var constructor = resterType.GetConstructors().FirstOrDefault();
if (constructor != null)
{
var parameters = constructor.GetParameters();
var parameterInstances = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
parameterInstances[i] = GetService(parameters[i].ParameterType);
var parameterType = parameters[i].ParameterType;
var service = GetService(parameterType);
if (service != null)
{
parameterInstances[i] = service;
}
else
{
throw new Exception($"Unable to resolve service of type {parameterType} for constructor of {resterType}.");
}
}
rester = (T)constructor.Invoke(parameterInstances);
rester = constructor.Invoke(parameterInstances);
}
var typeName = type.Name;
var methods = type.GetMethods();
foreach (var method in methods)
else
{
var attributes = method.GetCustomAttributes(typeof(HttpMethodAttribute), false);
if (attributes.Length > 0)
{
var attribute = attributes[0] as HttpMethodAttribute;
rester = Activator.CreateInstance(resterType);
}
// if route is null, use the route from the class name
if (string.IsNullOrEmpty(route))
{
route = Path.Combine("/", typeName.Split(new[] { "Rester" },
StringSplitOptions.None).FirstOrDefault());
}
var args = GetArguments(method, context);
var result = method.Invoke(rester, args);
var routeUrl = (route + attribute.route).ToLower();
MapRoute(routeUrl, async context =>
{
var response = context.Response;
response.ContentType = "application/json";
if (context.Request.HttpMethod == attribute.HttpMethodName)
{
var result = method.Invoke(rester, null);
var content = Encoding.UTF8.GetBytes(result.ToString());
response.ContentLength64 = content.Length;
using (var output = response.OutputStream)
{
await output.WriteAsync(content, 0, content.Length);
}
}
else
{
response.StatusCode = (int)HttpStatusCode.NotFound;
response.Close();
}
});
}
if (result is Task task)
{
await task;
}
}
/// <summary>
/// Using middleware
/// </summary>
/// <param name="middleware"></param>
/// <returns></returns>
public TigernetHostBuilder UseAsync(Func<HttpListenerContext, Task> middleware)
{
_middlewares.Add(middleware);
var response = context.Response;
var content = Encoding.UTF8.GetBytes(result.ToString());
response.ContentLength64 = content.Length;
using (var output = response.OutputStream)
{
await output.WriteAsync(content, 0, content.Length);
}
};
}

return this;
private object[] GetArguments(MethodInfo method, HttpListenerContext context)
{
var parameters = method.GetParameters();
var args = new object[parameters.Length];

for (int i = 0; i < parameters.Length; i++)
{
var parameterType = parameters[i].ParameterType;
if (parameterType == typeof(HttpListenerContext))
{
args[i] = context;
}
else
{
args[i] = null;
}
}

return args;
}
}
}

0 comments on commit b54328d

Please sign in to comment.