ASP.NET MVC — ActionFilterAttribute для проверки данных POST

На самом деле у меня есть приложение, которое использует WebService для получения информации о некоторых клиентах. Итак, я проверял информацию для входа в свой ActionResult, например:

[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");
    }
}

Где Client.Validate() — это метод, который возвращает логическое значение на основе информации, предоставленной для имени пользователя и пароля POST.

Но я передумал и хотел бы использовать эти приятные атрибуты ActionFilterAttributes в начале метода, чтобы он просто отображался, если Client.validate() возвращает true, точно так же, как [Authorize], но с моей пользовательской веб-службой, поэтому У меня было бы что-то вроде:

[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')
}

а затем внутри ValidateAsClient у меня будет что-то вроде:

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

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

Итак, моя проблема в том, что я точно не знаю, как заставить это работать, потому что я не знаю, как передать ОТПРАВЛЕННУЮ информацию в [ValidateAsClient(username=postedUsername,password=postedPassword)] а также, как я могу заставить функцию ValidateAsClient работать правильно?

Я надеюсь, что это легко понять Спасибо заранее


person zanona    schedule 21.10.2009    source источник


Ответы (4)


Что-то вроде этого, наверное:

[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
    }
}

И используйте это так:

[ValidateAsClient(HttpContext.Request.Form)]
person Egor Pavlikhin    schedule 21.10.2009
comment
Я думаю, вы могли бы получить доступ к коллекции форм с помощью filterContext.HttpContext.Request.Form вместо того, чтобы передавать ее. - person Çağdaş Tekin; 21.10.2009
comment
спасибо за то, что HeavyWave очень хорош, еще один вопрос: есть ли разница между использованием ActionExecutingContext и ActionExecutedContext в этом случае? Спасибо - person zanona; 21.10.2009
comment
Предполагается, что ActionExecutedContext используется в методе OnActionExecuted, который выполняется после метода действия контроллера. Таким образом, в ActionExecutedContext у вас есть доступ к некоторым результатам выполнения. Просто поэкспериментируйте с IntelliSense. - person Egor Pavlikhin; 21.10.2009

Вы должны переопределить следующий метод.

public override void OnActionExecuting(ActionExecutingContext context)

И из объекта контекста получите доступ к своим данным записи.

person J.W.    schedule 21.10.2009
comment
Проверьте ActionExecutingContext.RequestContext.HttpContext.Request.Form, и вы сможете получить там значение сообщения. - person J.W.; 21.10.2009

Я не думаю, что в этом случае лучше использовать ActionFilterAttribute. И то, что вы хотите сделать, определенно не совпадает с атрибутом Authorize.

Атрибут Authorize просто внедряет общую логику в контроллер/действие. Который :

Перенаправить на страницу входа, если пользователь не вошел в систему. В противном случае позвольте действию быть выполненным.

Ваше действие ClientLogin делает именно то, что оно должно делать в данный момент.
Было бы нехорошо переносить эту логику на ActionFilterAttribute.

person Çağdaş Tekin    schedule 21.10.2009
comment
Вы правы, не стоит превращать в атрибут то, что должно использоваться только одним действием. Authorize указывает, что действие требует авторизации пользователя, оно не содержит никакой логики. - person Egor Pavlikhin; 21.10.2009
comment
Да, я понимаю, что вы говорите, на самом деле проблема в том, что у меня будет несколько разных действий, которые будут выполняться клиентом во всем приложении, и для этого потребуется, чтобы клиент вошел в систему, иначе он будет перенаправлен на логин страница. Поэтому я подумал, что может быть намного проще и красивее (как я могу сказать на данный момент, так как я начал работать с ASP.NET, так что извините за что-то неправильное) поместить [ValidateAsClient] в начале каждого метода. Но я не уверен, что это правильно, спасибо за ваш вклад - person zanona; 21.10.2009
comment
Ну, ничего не сломаешь, если поступишь по-твоему. :) Просто не совсем естественно использовать атрибут таким образом. Это все. - person Çağdaş Tekin; 21.10.2009

Я бы решил эту проблему с помощью специального связующего в ASP.NET MVC.

Предположим, ваше действие будет иметь следующую подпись.

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

Класс MyParam:

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

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

}

Затем пользовательское связующее

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.
        }
}
person Eugeniu Torica    schedule 21.10.2009
comment
это над дизайном для простой задачи. @HeavyWave предоставил отличное и простое решение. - person reflog; 18.07.2010
comment
Тсс ... я просто не знал других способов сделать это :). - person Eugeniu Torica; 20.07.2010