Archive

Archive for the ‘ASP.NET’ Category

jQuery File Upload: IE9 and ASP.Net Web API File Uploading

June 2nd, 2016 No comments

    The blueimp jQuery File Upload plugin uses the XMLHttpRequest to pass the file data to a server (only IE10+). If browser doesn’t support Ajax file uploading, the plugin makes a workaround by dynamically creating IFrame and sending the data on behalf of it through the traditional form POST. The JavaScript responsible for the workaround resides in jquery.iframe-transport.js, which accompanies the basic jquery.fileupload.js. The following TypeScript code could be used to initialize the plugin and submit file data (the code is intentionally kept as simple as possible – no progress bars, validations and so on):

//...
private fileData: any = {};
//...
$(".filePicker").fileupload({
	autoUpload: false, // will be submitted once button is clicked
	method: "PUT",
	dataType: "json", 
	url: "api/someController/someMethod", // Web Api method to receive and process the file
	formData: () => { // additional parameters accompanying the file data
		return [{
			name: "bookName",
			value: $("#bookName").val()
		},
		{
			name: "bookGenre",
			value: $("#bookGenre").val()
		},
		{
			name: "bookAuthor",
			value: $("#bookAuthor").val()
		}];
	},
	add: (e: JQueryEventObject, data: any) => { // event handlers
		this.fileData = data;
		//...
	},
	done: (e: JQueryEventObject, data: any) => {
		//...
	},
	fail: (e: JQueryEventObject, data: any) => {		
		//...
	},
	always: (e: JQueryEventObject, data: any) => {
		//...
	}
});
//...
$("#loadBook").on('click', function () { // the button to initiate the file sending
	if (this.fileData && this.fileData.hasOwnProperty("process")) {

		// file data validation: size, extension, whatever else...
		
		this.fileData.process().done(() => { // file sending
			this.fileData.submit();
		});
	}
});

On the server side the following code receives and processes the file data:

using System;
using System.Text;
using System·Web;
using System·Web.Http;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Linq;
...
namespace DotNetFollower.Web.Controllers.Api
{
    [RoutePrefix("api/someController")]
	public class someController : ApiController
	{        
        public someController()
		{
            //...
		}
		
        [Route("someMethod")]
        [HttpPut]
        [HttpPost] // this attribute allows processing traditional form POST
        public ServiceResult<BookOutput> someMethod()
        {
            try
            { 
			    //...
			    var request = HttpContext.Current.Request;
			    var files   = request.Files;

			    if (files.Count == 0) 
				    throw new Exception("Couldn't find a book to load!");

			    // read accompanying parameters
			    var bookName   = request.Form.Get("bookName");
			    var bookGenre  = request.Form.Get("bookGenre");
			    var bookAuthor = request.Form.Get("bookAuthor");
			
			    var file = new HttpPostedFileWrapper(files[0]);
			
			    // parsing file.InputStream ...
			
			    // processing the parsed file data ...
			
			    file.InputStream.Close();
			    //...
			
			    return new ServiceResult<BookOutput>() 
				    { 
					    Data = new BookOutput() 
						    { 
							    Name   = bookName, 
							    Genre  = bookGenre, 
							    Author = bookAuthor 
						    } 
				    };
            }
            catch(Exception ex)
            {
                return new ServiceResult<BookOutput>(ex);
            }
        }
	}
}

// Where ServiceResult and BookOutput are defined as follows

public class ServiceResult<T>
{
	public bool   Success      { get; set; }
	public string ErrorMessage { get; set; }	
	public T      Data         { get; set; }

	public ServiceResult()
	{
		Success = true;
	}

	public ServiceResult(string errorMessage)
	{   
		ErrorMessage = errorMessage;
	}

	public ServiceResult(Exception exception)
	{
		ErrorMessage = exception.Message;	
	}
}

public class BookOutput
{
	public string Name   { get; set; }
	public string Genre  { get; set; }
	public string Author { get; set; }
}

Unfortunately, the code doesn’t works as expected in Internet Explorer 9 (thankfully, the lower versions are not supposed to be supported by the project, so I don’t care about them). If IE9 sends an Ajax request to the Web Api method, it interprets the JSON response correctly. However, when uploading a file, the jQuery File Upload plugin sends traditional non-Ajax form POST. So, having received the JSON result, IE9 prompts for a JSON file download.

Download Json File Prompt

To bypass such IE9 behaviour the server response should contain the content-type header “text/html” rather than the “application/json” returned by the Web Api method by default. To avoid writing some IE9 specific logic on both server and client sides, I’ve introduced the following Web Api method-adapter:

[Route("someMethodAdapted")]
[HttpPut]
[HttpPost] // this attribute allows processing traditional form POST
public HttpResponseMessage someMethodAdapted()
{
	var res = someMethod(); // call the original method

	const string JsonContentType = "application/json";
	const string HtmlContentType = "text/html";

	var response = Request.CreateResponse(HttpStatusCode.OK); // return 200 OK
	var context  = HttpContext.Current;
	response.Content = new StringContent(ToJson(res), // serialize result object into JSON
	  Encoding.UTF8, 
	  // check if the JSON content-type is accepted 
	  // (it's not accepted in case of form POST coming from IE9)
	  context.Request.AcceptTypes.Contains(JsonContentType, StringComparer.OrdinalIgnoreCase) ?
	     JsonContentType : HtmlContentType); // return suitable content-type

	return response;
}

// the ToJson method is defined as follows

private static string ToJson<T>(T obj) where T : class
{
	if (obj == null)
		return string.Empty;

	DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
	using (MemoryStream stream = new MemoryStream())
	{
		serializer.WriteObject(stream, obj);
		return Encoding.Default.GetString(stream.ToArray());
	}
}

The someMethodAdapted is supposed to be used instead of someMethod everywhere on the client side. So, repoint the url to the method-adapter

...
$(".filePicker").fileupload({
	...
	// Web Api method-adapter to receive and process the file
	url: "api/someController/someMethodAdapted",
	...
});
...

The someMethodAdapted makes a content-type trick and perfectly serves IE9 and higher. The use of HttpResponseMessage gives a control over the response headers. The HttpContext.Current.Request.AcceptTypes is a list of client-supported content types (aka MIME types). If the “application/json” is not in the list, the “text/html” is the right choice. Below are the AcceptTypes of Ajax and non-Ajax requests made by IE9:

// IE9 Ajax request to a Web Api method (true for higher browser versions too)
HttpContext.Current.Request.AcceptTypes	{string[3]}	string[]
[0]	"application/json"	string
[1]	"text/javascript"	string
[2]	"*/*; q=0.01"	string

// IE9 non-Ajax form POST
HttpContext.Current.Request.AcceptTypes	{string[3]}	string[]
[0]	"text/html"	string
[1]	"application/xhtml+xml"	string
[2]	"*/*"	string

ASP.NET MVC: Disable Controller

August 3rd, 2015 No comments

    If you want to disable a MVC Controller, not deleting it, a good way to do that is apply a custom ActionFilter-derived attribute to it. Don’t confuse the ActionFilterAttribute defined in the System.Web.Mvc (the one we’re going to use) with the attribute of the same name residing within the Web API infrastructure, namely in the System.Web.Http.Filters namespace.

So, the custom attribute could be defined like

public class DisableControllerAttribute : ActionFilterAttribute
{
	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		//filterContext.Result = new HttpNotFoundResult();
		throw new HttpException((int)HttpStatusCode.NotFound, null);
	}
}

Applying it to the target MVC Controller is as simple as follows

...
using System.Web.Mvc;
...

[DisableController]
public class SomeLegacyController : Controller
{
}

Calling any action of such MVC contoller, user gets the 404 Http Status Code page. The page could look differently depending on which way you use to return the Status Code. Using the first line of code in the DisableControllerAttribute.OnActionExecuting (currently commented), the IIS “native” 404 page will be returned.

IIS Native 404 page not found

Throwing the HttpException like shown in the second line of the DisableControllerAttribute.OnActionExecuting (uncommented), you’ll get the ASP.NET handled page.

ASP.Net 404 page not found

SharePoint: SqlMembershipProvider – Lock User

July 21st, 2013 No comments

    In addition to the article SharePoint: SqlMembershipProvider – Get All Users In Role, here is one more method to extend the SqlMembershipProvider with. It’s found out that the SqlMembershipProvider doesn’t provide a method to lock user. By default a user can be automatically locked after several frequent and failed attempts to login. To unlock such users the SqlMembershipProvider supplies with the UnlockUser method. But what if administrator wants to temporarily lock user for some reason? Unfortunately, there is no such method out-of-box.

So, let’s try to implement our own LockUser method. Two obvious steps for that are as follows: to create a Stored Procedure in database; to extend a class derived from the SqlMembershipProvider with the proper method.

LockUser Stored Procedure

The stored procedure is very simple as we need just to update one field in the aspnet_Membership table for appropriate user. Below is the script to create such procedure. Run the script on MembershipProvider database, in my case it’s aspnetdb.

USE [aspnetdb]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:      .Net Follower
-- Description:	Locks User
-- =============================================
CREATE PROCEDURE [dbo].[aspnet_Membership_LockUser]
	@ApplicationName                         nvarchar(256),
    @UserName                                nvarchar(256)
AS
BEGIN
	DECLARE @UserId uniqueidentifier
    SELECT  @UserId = NULL
    SELECT  @UserId = u.UserId
    FROM    dbo.aspnet_Users u, dbo.aspnet_Applications a, dbo.aspnet_Membership m
    WHERE   LoweredUserName = LOWER(@UserName) AND
            u.ApplicationId = a.ApplicationId  AND
            LOWER(@ApplicationName) = a.LoweredApplicationName AND
            u.UserId = m.UserId

    IF ( @UserId IS NULL )
        RETURN 1

    UPDATE dbo.aspnet_Membership 
    SET IsLockedOut = 1 WHERE @UserId = UserId

    RETURN 0
END

Custom Membership Provider

Now we can add the LockUser method to the custom Membership Provider called SqlMembershipProviderEx and shown in the article. The SqlMembershipProviderEx with the LockUser is listed below. Note that the methods mentioned in the previous article are skipped.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Reflection;
using System.Web.Security;
using Microsoft.SharePoint;

namespace dotNetFollower
{
    public class SqlMembershipProviderEx : SqlMembershipProvider
    {       
	    ...
		
        public bool LockUser(string username)
        {
            bool flag = false;
            CheckParameter(ref username, true, true, true, 0x100, "username");

            DoInSqlConnectionContext(delegate(SqlConnection connection)
            {
                //this.CheckSchemaVersion(connection.Connection);
                SqlCommand command = new SqlCommand("dbo.aspnet_Membership_LockUser", connection)
                {
                    CommandTimeout = CommandTimeout,
                    CommandType    = CommandType.StoredProcedure
                };
                command.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                command.Parameters.Add(CreateInputParam("@UserName", SqlDbType.NVarChar, username));
                SqlParameter parameter = new SqlParameter("@ReturnValue", SqlDbType.Int)
                {
                    Direction = ParameterDirection.ReturnValue
                };
                command.Parameters.Add(parameter);

                command.ExecuteNonQuery();
                flag = ((parameter.Value != null) ? ((int)parameter.Value) : -1) == 0;
            });

            return flag;
        }

        protected internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
        {
            if (param == null)
            {
                if (checkForNull)
                    throw new ArgumentNullException(paramName);
            }
            else
            {
                param = param.Trim();
                if (checkIfEmpty && (param.Length < 1))
                    throw new ArgumentException(string.Format("The parameter '{0}' must not be empty.", new object[] { paramName }), paramName);

                if ((maxSize > 0) && (param.Length > maxSize))
                    throw new ArgumentException(string.Format("The parameter '{0}' is too long: it must not exceed {1} chars in length.", new object[] { paramName, maxSize.ToString(CultureInfo.InvariantCulture) }), paramName);

                if (checkForCommas && param.Contains(","))
                    throw new ArgumentException(string.Format("The parameter '{0}' must not contain commas.", new object[] { paramName }), paramName);
            }
        }

        ...
        
    }    
}

The latest version of the SqlMembershipProviderEx along with all used additional classes are available to download here.

Related posts:

SharePoint: SqlMembershipProvider – Get All Users In Role

June 30th, 2013 No comments

    In the SharePoint application I’m currently working on, I configured Form Based Authentication (FBA) using the SqlMembershipProvider and SqlRoleProvider. Implementing some user management functionality, I run into the lack of a method to get the users in particular role by portions (so-called pagination). The SqlRoleProvider exposes the GetUsersInRole method which returns only names of users in the passed role and doesn’t support pagination. The direct way in this case is to get user names and then get appropriate users, calling the GetUser method of SqlMembershipProvider one time per name. This approach results in a bunch of requests to the database: one request is to get names of users in a role and a number of requests are to get each user by his name. In addition, we have somehow to implement pagination ourselves. The approach is acceptable, but let’s try to reduce requests to the database and borrow somewhere the pagination logic.

GetAllUsersInRole Stored Procedure

It’s interesting that the SqlMembershipProvider provides the GetAllUsers method that supports pagination. On the database level, every call of SqlMembershipProvider.GetUsersInRole and SqlMembershipProvider.GetAllUsers ends with executing such Stored Procedures as aspnet_UsersInRoles_GetUsersInRoles and aspnet_Membership_GetAllUsers respectively. So, we know that the aspnet_UsersInRoles_GetUsersInRoles searches for names of users in a role while the aspnet_Membership_GetAllUsers is able to return users by portions. Let’s combine these two Stored Procedures and create another one which would select users in a role and return a required portion of the result. The sql script below creates such Stored Procedure, I named it aspnet_Membership_GetAllUsersInRole. Note the script should be executed on MembershipProvider database, it’s aspnetdb in my case.

USE [aspnetdb]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:		.Net Follower
-- Description:	Returns users in role by portions
-- =============================================
CREATE PROCEDURE [dbo].[aspnet_Membership_GetAllUsersInRole]
    @ApplicationName       nvarchar(256),
    @PageIndex             int,
    @PageSize              int,
    @RoleName              nvarchar(256)
AS
BEGIN
    DECLARE @ApplicationId uniqueidentifier
    SELECT  @ApplicationId = NULL
    SELECT  @ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName
    IF (@ApplicationId IS NULL)
        RETURN 0

	DECLARE @RoleId uniqueidentifier
    SELECT  @RoleId = NULL

    SELECT  @RoleId = RoleId
    FROM    dbo.aspnet_Roles
    WHERE   LOWER(@RoleName) = LoweredRoleName AND ApplicationId = @ApplicationId

    IF (@RoleId IS NULL)
		RETURN 0

    -- Set the page bounds
    DECLARE @PageLowerBound int
    DECLARE @PageUpperBound int
    DECLARE @TotalRecords   int
    SET @PageLowerBound = @PageSize * @PageIndex
    SET @PageUpperBound = @PageSize - 1 + @PageLowerBound

    -- Create a temp table TO store the select results
    CREATE TABLE #PageIndexForUsers
    (
        IndexId int IDENTITY (0, 1) NOT NULL,
        UserId uniqueidentifier
    )

    -- Insert into our temp table
    INSERT INTO #PageIndexForUsers (UserId)
    SELECT u.UserId
    FROM   dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_UsersInRoles ur
    WHERE  u.ApplicationId = @ApplicationId AND u.UserId = m.UserId AND 
		   u.UserId = ur.UserId AND @RoleId = ur.RoleId
    ORDER BY u.UserName

    SELECT @TotalRecords = @@ROWCOUNT

    SELECT u.UserName, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
            m.CreateDate,
            m.LastLoginDate,
            u.LastActivityDate,
            m.LastPasswordChangedDate,
            u.UserId, m.IsLockedOut,
            m.LastLockoutDate
    FROM   dbo.aspnet_Membership m, dbo.aspnet_Users u, #PageIndexForUsers p
    WHERE  u.UserId = p.UserId AND u.UserId = m.UserId AND
           p.IndexId >= @PageLowerBound AND p.IndexId <= @PageUpperBound
    ORDER BY u.UserName
    RETURN @TotalRecords
END

Custom Membership Provider

Now let’s extend our Membership Provider with a new method that deals with the aspnet_Membership_GetAllUsersInRole. I created a class SqlMembershipProviderEx derived from SqlMembershipProvider and containing the target GetAllUsersInRole method. The class is demonstrated below, but first of all a few remarks on the code:

  • I had to use Reflection to get values of some important fields (like Connection String to the database, for example) as Microsoft makes everything private or internal;
  • The SqlMembershipProvider elevates privileges when opening SqlConnection. Since the extended Membership Provider is going to be used in SharePoint application, I did the same by means of SPSecurity.RunWithElevatedPrivileges. Note however that if you want to use the extended Membership Provider in a pure ASP.Net application you will need to deal with such internal (of course) classes as SqlConnectionHolder and ApplicationImpersonationContext through Reflection;
  • The code of GetAllUsersInRole method is mainly based on the GetAllUsers of the parent SqlMembershipProvider class.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Web.Security;
using Microsoft.SharePoint;

namespace dotNetFollower
{
    public class SqlMembershipProviderEx : SqlMembershipProvider
    {
        protected string _connectionString;
        protected int?   _sqlCommandTimeout;

        protected string ConnectionString
        {
            get 
            { 
                return _connectionString ?? 
                    (_connectionString = Convert.ToString(this.GetFieldValue("_sqlConnectionString"))); 
            }
        }

        protected int CommandTimeout
        {
            get
            {
                if (_sqlCommandTimeout == null)
                    _sqlCommandTimeout = Convert.ToInt32(this.GetFieldValue("_CommandTimeout"));
                return _sqlCommandTimeout.Value;
            }
        }

        public MembershipUserCollection GetAllUsersInRole(string role, int pageIndex, int pageSize, out int totalRecords)
        {
            if (pageIndex < 0)
                throw new ArgumentException("The pageIndex must be greater than or equal to zero.", "pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("The pageSize must be greater than zero.", "pageSize");
            
            long num = ((pageIndex * pageSize) + pageSize) - 1;
            if (num > 0x7fffffff)
                throw new ArgumentException("The combination of pageIndex and pageSize cannot exceed the maximum value of System.Int32.", "pageIndex and pageSize");
            
            MembershipUserCollection users = new MembershipUserCollection();
            int recordsAmount = 0;

            DoInSqlConnectionContext(delegate(SqlConnection connection)
                {
                    //this.CheckSchemaVersion(connection.Connection);
                    SqlCommand command     = new SqlCommand("dbo.aspnet_Membership_GetAllUsersInRole", connection);
                    SqlDataReader reader   = null;
                    SqlParameter parameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
                    command.CommandTimeout = CommandTimeout;
                    command.CommandType    = CommandType.StoredProcedure;
                    command.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                    command.Parameters.Add(CreateInputParam("@PageIndex", SqlDbType.Int, pageIndex));
                    command.Parameters.Add(CreateInputParam("@PageSize", SqlDbType.Int, pageSize));
                    command.Parameters.Add(CreateInputParam("@RoleName", SqlDbType.NVarChar, role));
                    parameter.Direction = ParameterDirection.ReturnValue;
                    command.Parameters.Add(parameter);
                    try
                    {
                        reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
                        while (reader.Read())
                        {
                            string   nullableString          = GetNullableString(reader, 0);
                            string   email                   = GetNullableString(reader, 1);
                            string   passwordQuestion        = GetNullableString(reader, 2);
                            string   comment                 = GetNullableString(reader, 3);
                            bool     boolean                 = reader.GetBoolean(4);
                            DateTime creationDate            = reader.GetDateTime(5).ToLocalTime();
                            DateTime lastLoginDate           = reader.GetDateTime(6).ToLocalTime();
                            DateTime lastActivityDate        = reader.GetDateTime(7).ToLocalTime();
                            DateTime lastPasswordChangedDate = reader.GetDateTime(8).ToLocalTime();
                            Guid     providerUserKey         = reader.GetGuid(9);
                            bool     isLockedOut             = reader.GetBoolean(10);
                            DateTime lastLockoutDate         = reader.GetDateTime(11).ToLocalTime();
                            users.Add(new MembershipUser(Name, nullableString, providerUserKey, email, passwordQuestion,
                                                         comment, boolean, isLockedOut, creationDate, lastLoginDate,
                                                         lastActivityDate, lastPasswordChangedDate, lastLockoutDate));
                        }
                    }
                    catch (Exception ex)
                    {
                        EventLogger.WriteError(ex);
                        throw;
                    }
                    finally
                    {
                        if (reader != null)
                            reader.Close();
                        if (parameter.Value is int)
                            recordsAmount = (int)parameter.Value;
                    }
                });
            totalRecords = recordsAmount;
            return users;
        }

        protected void DoInSqlConnectionContext(Action<SqlConnection> action)
        {
            SqlConnection connection = null;
            try
            {
                connection = new SqlConnection(ConnectionString);
                SPSecurity.RunWithElevatedPrivileges(connection.Open);
                action(connection);
            }
            finally
            {
                if (connection != null)
                    connection.Close();
            }
        }

        protected SqlParameter CreateInputParam(string paramName, SqlDbType dbType, object objValue)
        {
            SqlParameter parameter = new SqlParameter(paramName, dbType);
            if (objValue == null)
            {
                parameter.IsNullable = true;
                parameter.Value      = DBNull.Value;
                return parameter;
            }
            parameter.Value = objValue;
            return parameter;
        }

        protected string GetNullableString(SqlDataReader reader, int col)
        {
            return !reader.IsDBNull(col) ? reader.GetString(col) : null;
        }
    }
}

Note the EventLogger class is described in the post SharePoint: Simple Event Logger while the GetFieldValue method is provided by ReflectionHelper described in the C#: How to set or get value of a private or internal field through the Reflection and C#: How to set or get value of a private or internal property through the Reflection.

The latest version of the SqlMembershipProviderEx along with all used additional classes are available to download here.

Related posts:

ASP.NET: How to trigger a validator on the client-side

August 27th, 2012 No comments

ValidatorValidate

The easiest way to trigger a validator on the client-side is to use the ValidatorValidate method provided by ASP.NET as a part of a Validation client-side API. You don’t need to include any js-files so as the all required scripts will be included automatically when you add a validator to your page. The typical use is something like this

var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];

ValidatorValidate(validator); // fire validation
ValidatorUpdateIsValid(); // update the global Page_IsValid flag

The ValidatorValidate makes the passed validator examine the input element bound to it and updates the validator‘s display (shows or hides its error message). I also recommend to call the ValidatorUpdateIsValid method, which updates the global variable Page_IsValid and by that may prevent the Html-form submit when the validator failed.

Below is a simple JavaScript function I use to force validation of a specified input. The function gets the input by the passed id, then goes through the all validators linked to the input and activates each of them. Finally, it updates the global Page_IsValid flag.

function ForceInputValidation(inputId) {
    var targetedControl = document.getElementById(inputId);    
    
    if (typeof(targetedControl.Validators) != "undefined") {        
        var i;
        for (i = 0; i < targetedControl.Validators.length; i++)
            ValidatorValidate(targetedControl.Validators[i]);
    }

    ValidatorUpdateIsValid();
}

ASP.Net places the initialization of validators at the end of the web page, so, it makes sense to handle the input‘s Validators-collection after the page has been loaded entirely. The following example demonstrates how to fire validation of the specified input right after the page has been loaded. jQuery is a best helper in that.

$(document).ready(function () {    
    ForceInputValidation('ctl00_PlaceHolderMain_info_textbox');
});

ValidatorEnable

As some blogs and forums recommend, you can also use another built-in function – ValidatorEnable, the code of which is shown below:

function ValidatorEnable(validator, enable) {
    validator.enabled = (enable != false);
    ValidatorValidate(validator);
    ValidatorUpdateIsValid();
}
// ...
// the typical use to fire validation is
var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];
ValidatorEnable(validator, true);

The ValidatorEnable calls sequentially the same ValidatorValidate and ValidatorUpdateIsValid functions we use above. Thus to activate one validator you can use, with no difference, either ValidatorEnable or combination of ValidatorValidate and ValidatorUpdateIsValid. But activating several validators (like in ForceInputValidation function) I advise employing the pair of the ValidatorValidate and ValidatorUpdateIsValid functions. That is because the ValidatorUpdateIsValid loops through all validators on the page. So, for best performance we should fire the all required validators and then call ValidatorUpdateIsValid once.

ps here is good source of information about the client-side validation.