I am working on a Asp.net MVC project and I am wondering if there is a way for the attributes to talk to other attributes.
For example, I have the following attributes
public class SuperLoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Do something super
}
}
public class NormalLoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Do something normal ONLY if the super attribute isn't applied
}
}
And I have the following controllers
[NormalLogger]
public class NormalBaseController : Controller
{
}
public class PersonController: NormalBaseController
{
}
[SuperLogger]
public class SuperController: NormalBaseControler
{
}
So basically, I want my SuperController to use SuperLogger and ignore NormalLogger (which was applied in the base), and PersonController should use the NormalLogger since it's not "overrided" by the SuperLogger. Is there a way to do that?
Thanks,
Chi
Why not just have SuperLoggerAttribute inherit from NormalLoggerAttribute and override the Log method?
I think this should work:
public enum LoggerTypes
{
Normal,
Super
}
public class LoggerAttribute : ActionFilterAttribute
{
public LoggerAttribute() : base()
{
LoggerType = LoggerTypes.Normal;
}
public LoggerAttribute(LoggerTypes loggerType) : base()
{
LoggerType = loggerType;
}
public LoggerTypes LoggerType
{
get;
set;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (LoggerType == LoggerTypes.Super)
{
//
}
else
{
//
}
}
Usage:
[Logger(LoggerTypes.Normal)]
or
[Logger(LoggerTypes.Super)]
Related
I created a custom ActionFilterAttribute which I call like this :
[ScopeActionFilter(acceptedScopes = new string[] { "Files.Upload" })]
public IActionResult Upload(IFormFile[] files)
{
}
Now, how do I find the value of acceptedScopes in the OnActionExecuting method ? And how do I check that acceptedScopes was passed to the ActionFilter ?
public class ScopeActionFilter : ActionFilterAttribute
{
public string[] acceptedScopes { get; set; }
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
ScopesRequiredByWebApiExtension.VerifyUserHasAnyAcceptedScope(actionContext.HttpContext, actionContext.ActionArguments["acceptedScopes"] as string[]);
}
}
how do I find the value of acceptedScopes in the OnActionExecuting method ?
In your code, we can find that you set the value for acceptedScopes property while you applying the ScopeActionFilter to action method, to get the value of acceptedScopes in the OnActionExecuting method, you can try:
public class ScopeActionFilter : ActionFilterAttribute
{
public string[] acceptedScopes { get; set; }
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
var args = acceptedScopes;
ScopesRequiredByWebApiExtension.VerifyUserHasAnyAcceptedScope(actionContext.HttpContext, args);
}
}
Test Result
string[] ActionArguments = ((ScopeActionFilter)actionContext.Filters.Where(t => t is ScopeActionFilter).First()).acceptedScopes;
will work
I need to be able to serve additional methods in my generic controller if controller implements specific interface or has additional attribute or something like that. Here is what I have right now:
public abstract class AnimalController
{
[HttpPost]
public void Walk() { }
}
public class DogController : AnimalController
{
[HttpPost]
public void Bark() { }
}
public class CatController : AnimalController
{
[HttpPost]
public void Mew() { }
}
public class MutantController : DogController
{
[HttpPost]
public void Mew() { }
}
Mutant should have abilities of both Dog and Cat, but C# does not allow to do
public class MutantController : DogController, CatController
{
}
I was wondering what is the best way to solve this in context of WebAPI? Essentialy I need to add additional web methods depending on certain abilities. One solution is to do something like this:
public abstract class AnimalController
{
[HttpPost]
public void Walk() { }
}
public class DogController : AnimalController, ICanBark
{
[HttpPost]
public void Bark() { }
}
public class CatController : AnimalController, ICanMew
{
[HttpPost]
public void Mew() { }
}
public class MutantController : AnimalController, ICanMew, ICanBark
{
[HttpPost]
public void Bark() { }
[HttpPost]
public void Mew() { }
}
This will result in lots of additional code. I was thinking maybe SOMEHOW add methods dynamically so WebAPI knows to use additional controller when I place attributes on main one? Or processes via methods on attribute?
[CanMew]
[CanBark]
public class MutantController : AnimalController
{
}
This is my code:
// Controller
[HttpGet("{id}")]
[MyFilter]
public async Task<MyCustomType> Load(string id)
{
return new MyCustomType(....);
}
// Custom attribute
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
// Can I have my MyCustomType result here?
}
}
I need to implement some special logic in case of specific property values of MyCustomType result.
Public class MyCustomType
{
// assuming that there will be more properties
public int Id { get; set; }
public string Name { get; set; }
}
// Now, Move to Controller method.
public class CustomController : Controller
{
[HttpGet({"id"})]
[MyFilter]
public async Task<MyCustomType> Load(string id)
{
// Do some async operations
// Or do some Db queries
// returning MyCustomType
MyCustomType typeToReturn = new MyCustomType();
typeToReturn.Id = 1;
typeToReturn.Name = "something";
return typeToReturn;
}
}
// Well here goes the attribute
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
// you have to do more digging i am using dynamic to get the values and result.
dynamic content = context.Result;
if (content != null)
{
dynamic values = content.Value;
}
}
}
EDIT changed the code and ran it in a dot net core project and i was able to get the values, how ever i have used dynamic you can dig more on it.
I have a Website which has several aspx pages that derives from a PageBase class. For example one of that is below:
public partial class Pages_Home_Default : PageBase
{
}
In some of these pages, I would like to prevent access UNLESS logged in. I can get whether client is logged in or not in my PageBase with a IsMember property.
I would like to use attibutes to achive that. For example:
[AuthenticationRequired(true)]
public partial class Pages_Home_Default : PageBaseList
{
}
[AttributeUsage(AttributeTargets.Class)]
public class AuthenticationRequired : Attribute
{
public AuthenticationRequired(bool isMemberRequired)
{
Value = isMemberRequired;
}
public bool Value { get; private set; }
}
and in the PageBase for example:
protected override void OnPreInit(EventArgs e)
{
//Retrieve the AuthenticationRequired attribue value and if not authenticated Redirect client to a login page if logged in, continue displaying the page
}
I also found this to get and read the attribute
System.Reflection.MemberInfo info = typeof(Pages_Home_Default);
object[] attributes = info.GetCustomAttributes(true);
But this is not practical when you want to do it on the BASE class instead of the DERIVED one.
Can this be done?
Thank you very much
If your using MVC, there's an att for that - AuthorizeAttribute.
If your using WebForms then you don't need to use an attribute, you can control this from the web.config using the authorization element.
Why don't you check it in the attribute itself?
[AttributeUsage(AttributeTargets.Class)]
public class AuthenticationRequired : Attribute
{
public AuthenticationRequired(bool isMemberRequired)
{
if(isMemberRequired && !HttpContext.Current.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
}
}
}
Ok. I combined the code I have given earlier with a simple line from other sources and here is the code I came up with:
[AttributeUsage(AttributeTargets.Class)]
public class AuthenticationRequired : Attribute
{
public AuthenticationRequired(bool isMemberRequired)
{
Value = isMemberRequired;
}
public bool Value { get; private set; }
}
public class Utility
{
public static T GetCustomAttribute<T>(Type classType) where T : Attribute
{
object Result = null;
System.Reflection.MemberInfo Info = classType;
foreach (var Attr in Info.GetCustomAttributes(true))
{
if (Attr.GetType() == typeof(T))
{
Result = Attr;
break;
}
}
return (T)Result;
}
}
public class PageBase : System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
AuthenticationRequired AttrAuth = Utility.GetCustomAttribute<AuthenticationRequired>(this.GetType());
if (AttrAuth != null && AttrAuth.Value)
{
if(!IsMember)
HttpContext.Current.Response.Redirect("Membership.aspx");
}
}
}
I frequently seems to come up to a situation where I have an abstract type which needs to be processed differently depending on which concrete implementation it has.
As an example, an abstract class Payment could be subclassed as class CreditCard or class StoredCredit. To actually process the payment, we want to use an implementation of
interface IPaymentTaker {
PaymentProcessingResult Process(Payment payment); }
i.e. either
class CreditCardPaymentTaker : IPaymentTaker { ... }
or
class StoredCreditPaymentTaker : IPaymentTaker { ... }
In the past I have injected an IDictionary into the parent component and then done
_paymentTakers[payment.GetType()].Process(payment);
The downside of this is that the IPaymentTaker implementations are not strongly typed enough, so the first bit of the Process method has to be:
Process(Payment payment)
{
var creditCardPayment = payment as CreditCardPayment;
if (creditCardPayment == null)
throw new Exception("Payment must be of type CreditCard");
}
I'm sure there must be a name for the pattern I'm trying to implement but I don't know what it is!
Ideally I would
(a) be able to instantiate the PaymentProcessor based just on the type of the Payment, without creating the dictionary;
(b) be able to have strongly typed PaymentProcessors that only accept the subclass they can use.
Does anyone have a neat way of solving this problem?
You can solve this with a visitor:
interface IPaymentVisitor {
void Visit(CreditCard payment);
void Visit(StoredCredit payment);
}
abstract class Payment {
public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
public override void Accept(IPaymentVisitor visitor) {
visitor.Visit(this);
}
}
class StoredCredit : Payment {
public override void Accept(IPaymentVisitor visitor) {
visitor.Visit(this);
}
}
class PaymentTaker : IPaymentVisitor, IPaymentTaker {
public void Visit(CreditCard payment) {
// ...
}
public void Visit(StoredCredit payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
If you still want to separate the different payment takers, or if your hierarchy jitters, you can use an acyclic visitor (pdf):
interface IPaymentVisitor {
}
interface IPaymentVisitor<TPayment> : IPaymentVisitor where TPayment : Payment {
void Visit(TPayment payment);
}
abstract class Payment {
public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
public override void Accept(IPaymentVisitor visitor) {
if (visitor is IPaymentVisitor<CreditCard>) {
((IPaymentVisitor<CreditCard>)visitor).Visit(this);
}
}
}
class StoredCredit : Payment {
public override void Accept(IPaymentVisitor visitor) {
if (visitor is IPaymentVisitor<StoredCredit>) {
((IPaymentVisitor<StoredCredit>)visitor).Visit(this);
}
}
}
class CreditCardPaymentTaker : IPaymentVisitor<CreditCard>, IPaymentTaker {
public void Visit(CreditCard payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
class StoredCreditPaymentTaker : IPaymentVisitor<StoredCredit>, IPaymentTaker {
public void Visit(StoredCredit payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
interface IPayment
{
IPaymentTaker Taker {get;}
}
class CreditCardPayment : IPayment
{
IPaymentTaker Taker{ get {return new CreditCardPaymentTaker();}}
}
payment.Taker.Process(payment);
Even though James' method is ideal, using an IoC container could be difficult. Here's my Reflection or dynamics based approach. Doing the following will allow you to still use an IoC to setup the mapping between the PaymentTaker and Payment.
public class Payment
{
}
public class CreditCardPayment : Payment
{
}
public class StoreCreditPayment : Payment
{
}
public interface IPaymentTaker
{
}
public interface IPaymentTaker<T> : IPaymentTaker
{
void Process(T payment);
}
public static class PaymentTaker
{
public static void Process(Payment payment)
{
var paymentType = payment.GetType();
// You would have these already setup and loaded via your IOC container...
var paymentTakers = new Dictionary<Type, IPaymentTaker>();
paymentTakers.Add(typeof(CreditCardPayment), new CreditCardPaymentTaker());
paymentTakers.Add(typeof(StoreCreditPayment), new StoreCreditPaymentTaker());
// Get the payment taker for the specific payment type.
var paymentTaker = paymentTakers[paymentType];
// Execute the 'Process' method.
paymentTaker.GetType().GetMethod("Process").Invoke(paymentTaker, new object[]{ payment });
// If .NET 4.0 - dynamics can be used.
// dynamic paymentTaker = paymentTakers[paymentType];
// paymentTaker.Process((dynamic)payment);
}
}
public class CreditCardPaymentTaker : IPaymentTaker<CreditCardPayment>
{
public void Process(CreditCardPayment payment)
{
Console.WriteLine("Process Credit Card Payment...");
}
}
public class StoreCreditPaymentTaker : IPaymentTaker<StoreCreditPayment>
{
public void Process(StoreCreditPayment payment)
{
Console.WriteLine("Process Credit Card Payment...");
}
}
And then you can use it like this:
var cc = new CreditCardPayment();
PaymentTaker.Process(cc);
If you can ensure the names of the Payment and PaymentTaker match you can use something like this:
Process(Payment payment)
{
String typeName = "YourPathToPaymentTakers." + payment.GetType().Name + "Taker";
Type type = typeof(IPaymentTaker).Assembly.GetType(typeName);
IPaymentTaker taker = (IPaymentTaker)Activator.CreateInstance(type);;
}
I have used this approach in the past, but if you do not have 100% control of the names of the classes this could be a problem.