views:

607

answers:

4

Hello there,

Actually I have an application that is using a WebService to retrieve some clients information. So I was validating the login information inside my ActionResult like:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ClientLogin(FormCollection collection)
{
    if(Client.validate(collection["username"], collection["password"]))
    {
        Session["username"] = collection["username"];
        Session["password"] = collection["password"];
        return View("valid");
    }
    else
    {
       Session["username"] = "";
       Session["password"] = "";
       return View("invalid");
    }
}

Where Client.Validate() is a method that returns a boolean based on the information provided on the POST username and password

But I changed my mind and I would like to use that nice ActionFilterAttributes at the beginning of the method so it will just be rendered if the Client.validate() return true, just the same as [Authorize] but with my custom webservice, so I would have something like:

[AcceptVerbs(HttpVerbs.Post)]
[ValidateAsClient(username=postedUsername,password=postedPassword)]
//Pass Posted username and password to ValidateAsClient Class
//If returns true render the view
public ActionResult ClientLogin()
{
    return View('valid')
}

and then inside the ValidateAsClient I would have something like:

public class ValidateAsClient : ActionFilterAttribute
{
    public string username { get; set; }
    public string password { get; set; }

    public Boolean ValidateAsClient()
    {
        return Client.validate(username,password);
    }
}

So my problem is, I don't know exactly how to make it work, because I don't know how to pass the POSTED information to the [ValidateAsClient(username=postedUsername,password=postedPassword)] and also, how could I make the function ValidateAsClient work properly?

I hope this is easy to understand Thanks in advance

+4  A: 

You should override the following method.

public override void OnActionExecuting(ActionExecutingContext context)

And from the context object, access your post data.

J.W.
Check ActionExecutingContext.RequestContext.HttpContext.Request.Form, and you should be able to grab the post value there.
J.W.
A: 

I would solve this problem with a custom binder in ASP.NET MVC.

Suppose your action will have the following signature.

public ActionResult MyAction(MyParameter param)
{
  if(param.isValid)
    return View("valid");
  else
    return View("invalid");
}

MyParam class:

    public class MyParameter
    {
      public string UserName{get;set;}
      public string Password {get;set;}

      public bool isValid
      {
        //check if password and username is valid.
      }

}

An then the custom binder

public class CustomBinder:IModelBinder
{
 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
           var p = new MyParam();
           // extract necessary data from the bindingcontext like
           p.UserName = bindingContext.ValueProvider["username"] != null
                        ? bindingContext.ValueProvider["username"].AttemptedValue
                        : "";
          //initialize other attributes.
        }
}
Jenea
this is an over design for a simple task. @HeavyWave provided an excellent and simple solution.
Reflog
Tss ... I simple didn't know other ways to do it :).
Jenea
+2  A: 

Something like this probably:

[AttributeUsage(AttributeTargets.All)]
public sealed class ValidateAsClientAttribute : ActionFilterAttribute
{
    private readonly NameValueCollection formData;
    public NameValueCollection FormData{ get { return formData; } }

    public ValidateAsClientAttribute (NameValueCollection formData)
    {
        this.formData = formData;
    }

    public override void OnActionExecuting
               (ActionExecutingContext filterContext)
    {
        string username = formData["username"];
        if (string.IsNullOrEmpty(username))
        {
             filterContext.Controller.ViewData.ModelState.AddModelError("username");
        }
        // you get the idea
    }
}

And use it like this:

[ValidateAsClient(HttpContext.Request.Form)]
HeavyWave
I think you could access to form collection with `filterContext.HttpContext.Request.Form`, instead of passing it over.
çağdaş
thanks for that HeavyWave very good, another question: Is there any difference between use ActionExecutingContext and ActionExecutedContext in this case?Thanks
ludicco
ActionExecutedContext is supposed to be used in the OnActionExecuted method, which executes after the controller's action method. So in ActionExecutedContext you have access to some of the results of the execution. Just play around with it with IntelliSense.
HeavyWave
+2  A: 

I don't think it's a good idea to use an ActionFilterAttribute in this case. And what you want to do is definitely not the same as Authorize attribute does.

The Authorize attribute just injects a common logic into a controller/action. Which is :

Redirect to login page, if the user is not logged in. Else let the action be executed.

Your ClientLogin action does just what it's supposed to do at the moment.
It would be a bad design to carry that logic over to an ActionFilterAttribute.

çağdaş
You are right, it is not a good idea to turn something that should only be used by one Action into an attribute. Authorize indicates that the Action requires the user to be authorized, it doesn't contain any logic.
HeavyWave
Yes, I understand you point, actually the problem is that I would have a couple of different actions to be performed by the client all over the app, and this would require the client to be logged in otherwise it would be redirect him to a login page.So I was thinking it could be much easier and beautiful (as I can say for now since I'm a started on ASP.NET, so sorry about anything wrong applied) to put a [ValidateAsClient] at the beginning of each method. But I'm not sure it this is correct, thanks for your inputs
ludicco
Well, it wouldn't break anything to do it your way. :) Just not entirely natural to use the attribute that way. That's all.
çağdaş