0

How to configure actions if I specify AdditionalFields for validation via RemoteAttribute to handle possible optional additional fields? Currently I try to have actions for possible combinations of optional fields and it does not work as show below.

I have a form that is bound to a model and one of the fields is a remote validation field for the username. When I run the program, I am getting an error. Here is my validation code:

[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class ValidationController : Controller
{
    public JsonResult IsUserNameAvailable(string userName)
    {
        // compute condition, only "fail" shown.
        return Json(string.Format("{0} is already taken.", userName), 
            JsonRequestBehavior.AllowGet);
    }

    public JsonResult IsUserNameAvailable(string userName, string UserId)
    {
        // compute condition using both name and ID, only "fail" shown.
        return Json(string.Format("{0} is already taken.", userName),
             JsonRequestBehavior.AllowGet);
    }
}

Here is my model:

public class EditUserAdministrationViewModel
{
    public int UserId { get; set; }

    [Required(ErrorMessage = "You must enter a first name.")]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "You must enter a user name.")]
    [Display(Name = "User Name")]
    [Remote("IsUserNameAvailable", "Validation", AdditionalFields = "UserId")]
    public string UserName { get; set; }
   
    // more fileds 
}

Indeed I get the runtime error (since there are 2 actions matching by name as covered in Routing: The current request for action [...] is ambiguous between the following action methods):

The current request for action 'IsUserNameAvailable' on controller type 'ValidationController' is ambiguous between the following action methods:

System.Web.Mvc.JsonResult IsUserNameAvailable(System.String) on type BdsManager.Controllers.ValidationController

System.Web.Mvc.JsonResult IsUserNameAvailable(System.String, System.String) on type BdsManager.Controllers.ValidationController

Community
  • 1
  • 1
Icemanind
  • 47,519
  • 50
  • 171
  • 296
  • What does the actual request or query string that is being sent look like? – Brad C Jun 15 '15 at 16:06
  • Do you really need to show sample of SQL injection in the post? I don't think it is relevant to your question. – Alexei Levenkov Jun 15 '15 at 16:14
  • @AlexeiLevenkov - My code is not open to SQL injection. The `GetBySqlStatement` parameterizes everything. – Icemanind Jun 15 '15 at 16:15
  • The question linked here is not a duplicate at all. The linked question has nothing to do with Remote Attribute Parameters and is all about routing. – Icemanind Jun 15 '15 at 16:17
  • Please than make sure to edit your question to clarify that - so far looks like basic problem with MVC routing matching 2 actions to request. Possibly you can cut down sample to remove SQL stuff altogehter at the same time. – Alexei Levenkov Jun 15 '15 at 16:20
  • Please check out my edit as I think it makes your intention clear and highlights the problem. – Alexei Levenkov Jun 15 '15 at 16:34
  • @AlexeiLevenkov - Thanks for the edit. Looks good now. I was trying to be thorough, but you're right. I don't need my actual SQL in there. – Icemanind Jun 15 '15 at 16:36

2 Answers2

1

Actually, IsUserNameAvailable(string userName) and IsUserNameAvailable(string userName, string UserId) really is the same thing from MVC's point of view (hence the term 'abiguous').

When it is called, .NET tries to fill in all the parameters using either query string parameters or items in the POST body. Since the string object is not nullable, it will pass an empty string, instead of null to UserId. Which means, that UserId is always present, as an empty string. And then .NET gets confused, because it has one function that it can call, by just supplying the string userName, but it also has another function it can call, by supplying both parameters.

Better is to create one function and check whether or not UserId is empty.

Steven Lemmens
  • 620
  • 5
  • 18
  • "really is the same thing" is somewhat stretch, but close enough :) Both indeed would be found when searching for route matching to `IsUserNameAvailable` and there is no way in ASP.Net MVC to disambiguate actions by list of parameters in the method. – Alexei Levenkov Jun 15 '15 at 16:13
  • I get your point. I have updated my answer somewhat to clarify more. – Steven Lemmens Jun 15 '15 at 21:24
0

When it receives a query such as /Validation/IsUserNameAvailable?userName=BOB&UserID=, MVC's model binder is confused because it does not know how to handle null/empty string params. Just change the param to an int and cast as necessary for your helper method:

public JsonResult IsUserNameAvailable(string userName, int UserId)
{
    var users = new BusinessLayer.BdsAdmin.Users();
    users.GetBySqlStatement("SELECT * FROM [User] WHERE [UserName]='{0}' AND [UserId]<>{1}", userName, (int)UserId);    // add some exception safe conversion of types here

    if (users.Count == 0)
    {
        return Json(true, JsonRequestBehavior.AllowGet);
    }

    string msg = string.Format("{0} is already taken and is not available.", userName);
    return Json(msg, JsonRequestBehavior.AllowGet);
}

Even better, combine them into one method with a nullable / optional param:

public JsonResult IsUserNameAvailable(string userName, int? UserId)
{
    var users = new BusinessLayer.BdsAdmin.Users();
    if (UserId.HasValue)
    {
        users.GetBySqlStatement("SELECT * FROM [User] WHERE [UserName]='{0}' AND [UserId]<>{1}", userName, UserId.Value);
    } else {
        users.GetBySqlStatement("SELECT * FROM [User] WHERE [UserName]='{0}'", userName);
    }

    if (users.Count == 0)
    {
        return Json(true, JsonRequestBehavior.AllowGet);
    }

    string msg = string.Format("{0} is already taken and is not available.", userName);
    return Json(msg, JsonRequestBehavior.AllowGet);
}
Brad C
  • 2,868
  • 22
  • 33