How to pass in Uri complex objects without using custom ModelBinders or any serialization?

Do you wonder why I publish such an article?
Well, recently it happened to me to ask to a legacy information system in which I was to find information of users only with their name and their first name…
Yes yes using their name first name:).

I had to send to the WebApi a list of name/first name, the whole paginated, how did I make it?

At the beginning I had the idea to create my search model object in JSON which I would have to serialize while passing it in the URL, then, I had the idea to create an object with a string property containing the list of the names/firstnames the whole “decorated with separators” then split it in the WebAPI with a custom ModelBinder. Finally I decided I’d rather understand how to pass in the Uri more or less complex objects without using of ModelBinder or serialisation?.

Here what I did:

I tested 2 scenarios, the first with a list of “Person” and a second with a little more complex object “ListOfPersons”.
Before starting, as a reminder, I just want to remind you how we pass in a Uri a string collection.

namespace QueryStringDemo.Models
{
    public class Person
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }
    }
}
namespace QueryStringDemo.Models
{
    public class ListOfPersons
    {
        public int? PageNumber { get; set; } 
        public int? PageSize { get; set; }
        public List Persons { get; set; }
    }
}
using QueryStringDemo.Models;
using System.Collections.Generic;
using System.Web.Http;

namespace QueryStringDemo.Controllers
{
    [RoutePrefix("api/demo")]
    public class DemoController : ApiController
    {
        [Route("simplelist")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]List strings)
        {
            return Ok();
        }

        [Route("simplelistOfpersons")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]List listOfPersons)
        {
            return Ok();
        }

        [Route("complexlistOfpersons")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]ListOfPersons listOfPersons)
        {
            return Ok();
        }
    }
}

1- Scenario 1, reminder, string collection :

Uri :

scenario1-url

Result in code :

scenario1-code

Note that you have to use the attribute [FromUri], it provides deserialization to the target object from a complex object that comes from the queryString.

2- Scenario 2, collection of person object :

Uri :

scenario2-url

Result in code :

scenario2-code-1

Note that the object is in fact an array, an array like this

?[index].PropertyName=Value

3- Scenario 3, a more complex collection of person object :

Uri :

scenario3-url

Result in code :

scenario3-code

Note that it works also with an array,
our object looks like this :

?PropertyName1=value&PropertyName2=value&PropertyName3[index].SubPropertyName1=value&PropertyName3[index].SubPropertyName2=value

Finally we have demystified how queryString native serialization/deserialization works, was it so complicated ? 🙂

How to Create Your Own Membership Provider Instead of Using SqlMembershipProvider

So you want to use the ASP.NET SQL Membership authentification system but you’re constrained by data access rules. Well you can create your own provider instead of using the default SqlMembershipProvider, no matter which way you access selected data.

For example, you are forced to use a webservice, or the database of users already exists. The following solution will allow you to handle such cases instead of appealing to SqlMembershipProvider.

Step 1: Identify data to be managed

Let’s consider the following data model, which we will assume is the data contract provided by a webservice:

   
public class User
{
	public long UserId { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string Email { get; set; }
	public string Password { get; set; }
	public string Address { get; set; }
	public string City { get; set; }
	public string ZipCode { get; set; }
	public string Country { get; set; }
	public bool IsActive { get; set; }
	public DateTime CreationDate { get; set; }
	public DateTime LastLoginDate { get; set; }
}

Let’s also consider the data access service, which contains this signature:

    
public class AuthenticationService : IDisposable
{
	public bool UserExists(string email, string password)
	{
		//implementation here
	}

	public User GetUser(string username)
	{
		//implementation here
	}

	public User GetUser(long userId)
	{
		//implementation here
	}

	public bool UpdateUser(User user)
	{
		//implementation here
	}

	public void Dispose()
	{
		//implementation here
	}
}

Step 2: Create a custom MembershipUser derived from the original MembershipUser​

As can be seen, this class has the contract “User” as a property.

    
public class CustomMemberShipUser : MembershipUser
{
	private readonly User _userData;

	public User UserData
	{
		get { return _userData; }
	}

	
	/// 
	/// Constructeur de la classe derivée du MemberShip
	/// 
	public CustomMemberShipUser(string providername, User userData) :
	base(providername,
		 userData.Email,
		 userData.UserId,
		 userData.Email,
		 string.Empty,
		 string.Empty,
		 true,
		 !userData.IsActive,
		 userData.CreationDate,
		 userData.LastLoginDate,
		 DateTime.Now,
		 DateTime.Now,
		 DateTime.Now)
	{
		this._userData = userData;
	}
}

Note that you will need to import “System.Web.Security” assembly.

I have used some data in the “User” contract to fill in required fields in the base class.

I have also decided that the username is the email “!userData.IsActive”, filled into the “isLockedOut” property in the base class.

Step 3: Create a custom MembershipProvider derived from the original MembershipProvider​

Note that you must implement all methods. Otherwise, you’ll need to create an override method and add in “throw new NotImplementedException();”.

    
public class CustomMemberShipProvider : MembershipProvider
{
	public override bool ValidateUser(string username, string password)
	{
		using (var service = new AuthenticationService())
		{
			return service.UserExists(username, password);
		}
	}

	public override MembershipUser GetUser(string username, bool userIsOnline)
	{
		using (var service = new AuthenticationService())
		{
			var user = service.GetUser(username);

			if (null != user)
				return new CustomMemberShipUser(Membership.Provider.ApplicationName, user);

			return null;
		}
	}

	public override void UpdateUser(MembershipUser userToUpdate)
	{
		var user = (CustomMemberShipUser) userToUpdate;
		using (var service = new AuthenticationService())
		{
			var result = service.UpdateUser(user.UserData);
			if (!result)
				throw new Exception("User has not been updated");
		}

	}

	public override string ApplicationName
	{
		get { return "MyAppMemberShip"; }
		set { throw new NotImplementedException(); }
	}

	public override bool ChangePassword(string username, string oldPassword, string newPassword)
	{
		throw new NotImplementedException();
	}

	///
	/// 
	/// all overrrided methods
	/// 
	/// 

}

Step 4: Define this custom MembershipProvider as the default Membership provider in your web.config as follows:​

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms">
      <forms loginUrl="~/Home/DoLogin" timeout="2880"/>
    </authentication>
    <membership defaultProvider="MyAppMemberShip">
      <providers>
        <clear/>
        <add name="MyAppMemberShip" type="MemberShip.CustomMemberShipProvider, MemberShip"/>
      </providers>
    </membership>
</system.web>

Note that “MemberShip.CustomMemberShipProvider, MemberShip” is my assembly where i have written my provider

Step 5: As the classical SqlMembershipProvider, add the authentication mode (with the login page url) to your web.config: ​

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms">
      <forms loginUrl="~/Home/DoLogin"/>
    </authentication>
    <membership defaultProvider="MyAppMemberShip">
      <providers>
        <clear/>
        <add name="MyAppMemberShip" type="MemberShip.CustomMemberShipProvider, MemberShip"/>
      </providers>
    </membership>
</system.web>

Step 6: Test your implementation!

Don’t forget to use the “FormsAuthentication” class in order to create the “.ASPXAUTH” cookie that is used by MembershipProvider to identify the user and manage the sign-out method. 🙂

    
public class HomeController : Controller
{
	public ActionResult DoLogin()
	{
		if (Membership.ValidateUser("myemail@gmail.com", "xxxxx"))
		{
			FormsAuthentication.SetAuthCookie("myemail@gmail.com", true);
			return Content("login success");
		}
		return Content("login error");
	}

	public ActionResult Index()
	{
		if (User.Identity.IsAuthenticated)
		{
			var user = (CustomMemberShipUser)Membership.GetUser();
			return Content("User connected!");
		}

		return RedirectToAction("DoLogin");
	}

	public void SignOut()
	{
		FormsAuthentication.SignOut();
	}

}

Here are the results after my demo execution:

capture_1_0.png

capture_2.png

Easy, isn’t it? 😉

How to Load an HTML View Other Than With an MVC View File?

Have you ever asked yourself if it was possible to display some HTML content in an ASP.NET MVC application, using another way than through a traditional MVC view? This article will cover that exactly.

The principle is to short circuit the views resolution mechanism.

Step 1 : Override the “VirtualPathProvider” class

We’ll override “FileExists” and “GetFile” methods and then create filters that will detect the views to short circuit.

In this example, I have created a private member “filters” that defines the list of the views we want to “catch” and load them using another way.

Then, we override “FileExists” and “GetFile” methods to catch our filters.

It’s necessary to simulate a “real” path for our views we want to catch, that’s what “PathFormatter”will do.

Finally, “GetViewBytesData” will load the short circuited content and send it in to another overrided class : “CustomVirtualFile”.

You can implement this method as you like, and load different views from anywhere (flat file, webservice, database…)

public class CustomVirtualPathProvider : VirtualPathProvider 
    {
        private static List filters = new List()
        {
            PathFormatter("PhantomView")
        };
               
        public override bool FileExists(string virtualPath)
        {
            if (!filters.Contains(virtualPath))
                return base.FileExists(virtualPath);
            return true;
        }
      
         public override VirtualFile GetFile(string virtualPath)
         {
            if(!filters.Contains(virtualPath))
                return base.GetFile(virtualPath);
            return new CustomVirtualFile(virtualPath, GetViewBytesData());
        }
       
        private byte[] GetViewBytesData()
        {
            string htmlBody = @"@{ Layout = null;}
                        <hgroup>  
                            <h1>@ViewBag.Title</h1>
                            <h2>@ViewBag.Message</h2>
 
                            <div>Message:      @Model.Message         </div>
                        </hgroup>";
            return Encoding.ASCII.GetBytes(htmlBody);
        }

        public static string PathFormatter(string viewName)
        {
            return string.Format(@"/Views/Shared/{0}.cshtml",viewName);
        }
    }

Step 2 : Implement a custom VirtualFile class derived from the abstract class “VirtualFile”

By implementing “Open” methods, this class allows you to inject content from anywhere instead of the classical MVC view approach:

public class CustomVirtualFile : VirtualFile
    {
        private readonly byte[] _data;

        public CustomVirtualFile(string virtualPath, byte[] data)
            : base(virtualPath)
        {
            _data = data;
        }

        public override Stream Open()
        {
            return new MemoryStream(_data);
        }
    }

Step 3 : Register our own VirtualPathProvider into the Global.asax file

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            HostingEnvironment.RegisterVirtualPathProvider(new CustomVirtualPathProvider());
        }

Step 4 : Create the folder “Shared” if it doesn’t exist (by default, it should becreated automatically when you create a new MVC project)

Step 5 : Create a controller and an action that render a “fake” view

public class PhantomController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Title"] = "The phantom view";
            ViewBag.Message = "Welcome into the phantom view!!!!";

            var model = new PhantomModel
            {
                Message = "Ouuuuh I don't exist!"
            };

            return View(CustomVirtualPathProvider.PathFormatter("PhantomView"), model);
        }
    }

As you can see, I’m using a classical model, ViewBag and and the ViewData.

Remember, the content of our “fake” view is defined in the “GetViewBytesData” on step 1

Step 6 : Test it!

phantom.png

Funny isn’t it? 🙂