-
-
Save composite/aac8858a04afe7a222f8 to your computer and use it in GitHub Desktop.
Nancy Attrubute-Driven Module Example (Nancy 1.x or above, targeted as .NET 4.5 or later.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace Nancy | |
{ | |
/// <summary> | |
/// Nancy Attribute Driver Module (Must inherit) | |
/// </summary> | |
public abstract class NancyAttributeModule : NancyModule | |
{ | |
/// <summary> | |
/// Class Base Route Path | |
/// </summary> | |
protected readonly string basePath; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="NancyAttributeModule"/> class. | |
/// </summary> | |
protected NancyAttributeModule() | |
: base() | |
{ | |
this.basePath = string.Empty; | |
Init(); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="NancyAttributeModule"/> class. | |
/// </summary> | |
/// <param name="path">The path.</param> | |
protected NancyAttributeModule(string path) | |
: base(path) | |
{ | |
this.basePath = path; | |
Init(); | |
} | |
/// <summary> | |
/// Initializes this instance. | |
/// </summary> | |
/// <exception cref="InvalidCastException">Attribute-Driven Method cannot cast from <see cref="NancyModule.RouteBuilder"/>.</exception> | |
/// <exception cref="MethodAccessException">Attribute-Driven Method is not accessible from this class.</exception> | |
/// <exception cref="InvalidOperationException">Attribute-Driven Module Initialization failed.</exception> | |
private void Init() | |
{ | |
Type type = this.GetType(), nct = typeof(NancyContext); | |
Type tattr = typeof(NancyRequestAttribute), battr = typeof(NancyBeforeFilterAttribute), aattr = typeof(NancyAfterFilterAttribute); | |
MethodInfo[] methods = type.GetMethods(); | |
Dictionary<string, Func<NancyContext, bool>> conditions = methods.Where(m=> m.GetCustomAttribute<NancyRequestConditionAttribute>(false)!=null) | |
.ToDictionary(method => method.GetCustomAttribute<NancyRequestConditionAttribute>(false).Name ?? method.Name, method => | |
{ | |
if(method.GetParameters().Length != 1 || method.ReturnType != typeof(bool)) | |
throw new InvalidCastException($"{type.FullName}.{method.Name} method must have a argument with Nancy.NancyContext and return type as System.Boolean."); | |
ParameterExpression pex = Expression.Parameter(typeof(NancyContext)); | |
return Expression.Lambda<Func<NancyContext, bool>>(Expression.Call(Expression.Constant(this), method, pex), pex).Compile(); | |
}); | |
foreach (MethodInfo method in methods) | |
{ | |
string strE = "'" + type.FullName + "." + method.Name + "' method {0}"; | |
Exception arglenE = new TargetParameterCountException(string.Format(strE, "must have 1 argument in normal use. if use Task or async, you must 2 arguments with any type and System.Threading.CancellationToken.")), | |
ncE = new InvalidCastException(string.Format(strE, "must have 1 argument as Nancy.NancyContext!")); | |
if (method.IsDefined(tattr, false)) | |
{ | |
ParameterInfo[] ps = method.GetParameters(); | |
if (method.ReturnType == typeof(void)) throw new InvalidCastException(string.Format(strE, "must have return any type!")); | |
foreach (NancyRequestAttribute attr in method.GetCustomAttributes(tattr, false)) | |
{ | |
if (attr == null) continue; | |
if (attr.Async) | |
{ | |
if (ps.Length != 2) throw arglenE; | |
if (method.ReturnType.BaseType != typeof(Task)) throw new InvalidCastException(string.Format(strE, "must have async and System.Threading.Task<dynamic> type!")); | |
ParameterExpression pex = Expression.Parameter(typeof(object)), pcx = Expression.Parameter(typeof(CancellationToken)); | |
var body = Expression.Lambda<Func<dynamic, CancellationToken, Task<dynamic>>>(Expression.Call(Expression.Constant(this), method, pex, pcx), pex, pcx).Compile(); | |
if (!string.IsNullOrEmpty(attr.Condition)) | |
{ | |
switch (attr.Type) | |
{ | |
case RequestType.Get: Get[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Post: Post[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Put: Put[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Delete: Delete[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Options: Options[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Patch: Patch[attr.Path, conditions[attr.Condition], true] = body; break; | |
case RequestType.Head: Head[attr.Path, conditions[attr.Condition], true] = body; break; | |
} | |
} | |
else | |
{ | |
switch (attr.Type) | |
{ | |
case RequestType.Get: Get[attr.Path, true] = body; break; | |
case RequestType.Post: Post[attr.Path, true] = body; break; | |
case RequestType.Put: Put[attr.Path, true] = body; break; | |
case RequestType.Delete: Delete[attr.Path, true] = body; break; | |
case RequestType.Options: Options[attr.Path, true] = body; break; | |
case RequestType.Patch: Patch[attr.Path, true] = body; break; | |
case RequestType.Head: Head[attr.Path, true] = body; break; | |
} | |
} | |
} | |
else | |
{ | |
if (ps.Length != 1) throw arglenE; | |
ParameterExpression pex = Expression.Parameter(typeof(object)); | |
var body = Expression.Lambda<Func<object, object>>(Expression.Call(Expression.Constant(this), method, pex), pex).Compile(); | |
if (!string.IsNullOrEmpty(attr.Condition)) | |
{ | |
switch (attr.Type) | |
{ | |
case RequestType.Get: Get[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Post: Post[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Put: Put[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Delete: Delete[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Options: Options[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Patch: Patch[attr.Path, conditions[attr.Condition]] = body; break; | |
case RequestType.Head: Head[attr.Path, conditions[attr.Condition]] = body; break; | |
} | |
} | |
else | |
{ | |
switch (attr.Type) | |
{ | |
case RequestType.Get: Get[attr.Path] = body; break; | |
case RequestType.Post: Post[attr.Path] = body; break; | |
case RequestType.Put: Put[attr.Path] = body; break; | |
case RequestType.Delete: Delete[attr.Path] = body; break; | |
case RequestType.Options: Options[attr.Path] = body; break; | |
case RequestType.Patch: Patch[attr.Path] = body; break; | |
case RequestType.Head: Head[attr.Path] = body; break; | |
} | |
} | |
} | |
} | |
} | |
else if (method.IsDefined(battr, false)) | |
{ | |
ParameterInfo[] ps = method.GetParameters(); | |
if (ps.Length != 1) throw arglenE; | |
if (ps.Any(param => param.ParameterType != nct)) throw ncE; | |
if (method.ReturnType != typeof(Response)) throw new MethodAccessException(string.Format(strE, "must return Nancy.Response!")); | |
ParameterExpression pex = Expression.Parameter(nct); | |
Func<NancyContext, Response> body = Expression.Lambda<Func<NancyContext, Response>>(Expression.Call(Expression.Constant(this), method, pex), pex).Compile(); | |
Before += body; | |
} | |
else if (method.IsDefined(aattr, false)) | |
{ | |
ParameterInfo[] ps = method.GetParameters(); | |
if (ps.Length != 1) throw arglenE; | |
if (ps.Any(param => param.ParameterType != nct)) throw ncE; | |
if (method.ReturnType != typeof(void)) throw new MethodAccessException(string.Format(strE, "must return NOTHING(void)!")); | |
ParameterExpression pex = Expression.Parameter(nct); | |
Action<NancyContext> body = Expression.Lambda<Action<NancyContext>>(Expression.Call(Expression.Constant(this), method, pex), pex).Compile(); | |
After += body; | |
} | |
} | |
//DriverModule Helper ViewBag | |
After += cx => | |
{ | |
ViewBag.ClassName = this.GetType().Name; | |
ViewBag.BasePath = this.basePath; | |
}; | |
} | |
} | |
/// <summary> | |
/// Nancy Request Attribute. you can define twice or more. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] | |
public class NancyRequestAttribute : Attribute | |
{ | |
/// <summary> | |
/// Defines Request for declared request type and route path. | |
/// </summary> | |
/// <param name="type"></param> | |
/// <param name="path"></param> | |
public NancyRequestAttribute(RequestType type, string path) | |
{ | |
this.Type = type; | |
this.Path = path; | |
} | |
/// <summary> | |
/// Defines Request for GET type and route path. | |
/// </summary> | |
/// <param name="path"></param> | |
public NancyRequestAttribute(string path) : this(RequestType.Get, path) { } | |
/// <summary> | |
/// Gets the request type. | |
/// </summary> | |
/// <value>The type.</value> | |
public RequestType Type { get; } | |
/// <summary> | |
/// Gets the request path. | |
/// </summary> | |
/// <value>The path.</value> | |
public string Path { get; } | |
/// <summary> | |
/// Gets or sets a value indicating whether this <see cref="NancyRequestAttribute"/> is asynchronous. | |
/// </summary> | |
/// <value><c>true</c> if asynchronous; otherwise, <c>false</c>.</value> | |
public bool Async { get; set; } | |
/// <summary> | |
/// Gets or sets the condition name. | |
/// </summary> | |
/// <value>The condition.</value> | |
public string Condition { get; set; } | |
} | |
/// <summary> | |
/// The Nancy GET alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class GetAttribute : NancyRequestAttribute | |
{ | |
public GetAttribute(string path) : base(path) { } | |
} | |
/// <summary> | |
/// The Nancy POST alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class PostAttribute : NancyRequestAttribute | |
{ | |
public PostAttribute(string path) : base(RequestType.Post, path) { } | |
} | |
/// <summary> | |
/// The Nancy PUT alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class PutAttribute : NancyRequestAttribute | |
{ | |
public PutAttribute(string path) : base(RequestType.Put, path) { } | |
} | |
/// <summary> | |
/// The Nancy DELETE alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class DeleteAttribute : NancyRequestAttribute | |
{ | |
public DeleteAttribute(string path) : base(RequestType.Delete, path) { } | |
} | |
/// <summary> | |
/// The Nancy PATCH alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class PatchAttribute : NancyRequestAttribute | |
{ | |
public PatchAttribute(string path) : base(RequestType.Patch, path) { } | |
} | |
/// <summary> | |
/// The Nancy OPTIONS alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class OptionsAttribute : NancyRequestAttribute | |
{ | |
public OptionsAttribute(string path) : base(RequestType.Options, path) { } | |
} | |
/// <summary> | |
/// The Nancy HEAD alias request attribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class HeadAttribute : NancyRequestAttribute | |
{ | |
public HeadAttribute(string path) : base(RequestType.Head, path) { } | |
} | |
/// <summary> | |
/// Nancy Request condition defination attribute. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public class NancyRequestConditionAttribute : Attribute | |
{ | |
/// <summary> | |
/// Gets the name. if null, method name will used. | |
/// </summary> | |
/// <value>The name.</value> | |
public string Name { get; set; } | |
} | |
/// <summary> | |
/// Nancy Request condition defination attribute. This class cannot be inherited. | |
/// Alias class of <see cref="NancyRequestConditionAttribute"/>. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class ConditionAttribute : NancyRequestConditionAttribute { } | |
/// <summary> | |
/// Nancy Before Filter Attribute | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public class NancyBeforeFilterAttribute : Attribute { } | |
/// <summary> | |
/// Nancy Before Filter Attribute. This class cannot be inherited. | |
/// Alias class of <see cref="NancyBeforeFilterAttribute"/>. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class BeforeAttribute : NancyBeforeFilterAttribute { } | |
/// <summary> | |
/// Nancy After Filter Attribute | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public class NancyAfterFilterAttribute : Attribute { } | |
/// <summary> | |
/// Nancy After Filter Attribute. This class cannot be inherited. | |
/// Alias class of <see cref="NancyAfterFilterAttribute"/>. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Method, Inherited = false)] | |
public sealed class AfterAttribute : NancyAfterFilterAttribute { } | |
/// <summary> | |
/// Nancy Request Types for Request Attribute | |
/// </summary> | |
public enum RequestType | |
{ | |
/// <summary> | |
/// HTTP Verb : GET | |
/// </summary> | |
Get, | |
/// <summary> | |
/// HTTP Verb : POST | |
/// </summary> | |
Post, | |
/// <summary> | |
/// HTTP Verb : PUT | |
/// </summary> | |
Put, | |
/// <summary> | |
/// HTTP Verb : DELETE | |
/// </summary> | |
Delete, | |
/// <summary> | |
/// HTTP Verb : PATCH | |
/// </summary> | |
Patch, | |
/// <summary> | |
/// HTTP Verb : OPTIONS | |
/// </summary> | |
Options, | |
/// <summary> | |
/// HTTP Verb : HEAD | |
/// </summary> | |
Head | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Nancy; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
using CustomerWanso.Secutiry; | |
namespace CustomerWanso.Controller | |
{ | |
public class MainModule : NancySecurityModule | |
{ | |
[NancyRequest("/")] | |
public dynamic GetMain(dynamic route) | |
{ | |
return View["Main.cshtml"]; | |
} | |
[NancyRequest("/async", Async = true)] | |
public async Task<dynamic> AsyncMain(dynamic route) | |
{ | |
return Response.AsText("<h1>It works!</h1> whooray!", "text/html;charset=utf-8"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
using Nancy; | |
using Nancy.Responses; | |
using CustomerWanso.DataModel; | |
namespace CustomerWanso.Secutiry | |
{ | |
public class NancySecurityModule : NancyAttributeModule | |
{ | |
public const string LOGINURL = "/Login"; | |
public const string USERSESS = "USER"; | |
public NancySecurityModule() : base() { } | |
public NancySecurityModule(string path) : base(path) { } | |
[NancyBeforeFilter] | |
public Response LoginFilter(NancyContext context) | |
{ | |
Response res = null; | |
if (context.Request.Session[USERSESS] as UserDto == null) | |
return new RedirectResponse(LOGINURL); | |
return res; | |
} | |
[NancyAfterFilter] | |
public void UserInfoFilter(NancyContext context) | |
{ | |
UserDto user = Session[USERSESS] as UserDto; | |
if (user != null) | |
{ | |
ViewBag.UserId = user.UserId; | |
ViewBag.UserName = user.UserName; | |
ViewBag.Status = user.Status; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment