Here's how I do it.
I decided to use IPrincipal instead of IIdentity because it means I don't have to implement both IIdentity and IPrincipal.
Create the interface
interface ICustomPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
CustomPrincipal
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public CustomPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
CustomPrincipalSerializeModel - for serializing custom information into userdata field in FormsAuthenticationTicket object.
public class CustomPrincipalSerializeModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
LogIn method - setting up a cookie with custom information
if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
{
var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
serializeModel.Id = user.Id;
serializeModel.FirstName = user.FirstName;
serializeModel.LastName = user.LastName;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(serializeModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
viewModel.Email,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
Response.Cookies.Add(faCookie);
return RedirectToAction("Index", "Home");
}
Global.asax.cs - Reading cookie and replacing HttpContext.User object, this is done by overriding PostAuthenticateRequest
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
HttpContext.Current.User = newUser;
}
}
Access in Razor views
@((User as CustomPrincipal).Id)
@((User as CustomPrincipal).FirstName)
@((User as CustomPrincipal).LastName)
and in code:
(User as CustomPrincipal).Id
(User as CustomPrincipal).FirstName
(User as CustomPrincipal).LastName
I think the code is self-explanatory. If it isn't, let me know.
Additionally to make the access even easier you can create a base controller and override the returned User object (HttpContext.User):
public class BaseController : Controller
{
protected virtual new CustomPrincipal User
{
get { return HttpContext.User as CustomPrincipal; }
}
}
and then, for each controller:
public class AccountController : BaseController
{
// ...
}
which will allow you to access custom fields in code like this:
User.Id
User.FirstName
User.LastName
But this will not work inside views. For that you would need to create a custom WebViewPage implementation:
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
Make it a default page type in Views/web.config:
<pages pageBaseType="Your.Namespace.BaseViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
and in views, you can access it like this:
@User.FirstName
@User.LastName
The answer I'm providing is just taking regular postbacks into account. All the below can be applied using the ScriptManager and its respective methods to do the same.
There's a couple of ways to do it. You can load a web resource and reference it
// The Scripts namespace in this case would actually be a physical folder in your YourNamespace.CustomControlsNamespace
// namespace.
// Also the /Scripts/YourJavaScriptFile.js needs to have it's Build Action property set to Embedded Resource
[assembly: WebResource("YourNamespace.CustomControlsNamespace.Scripts.YourJavaScriptFile.js", "text/javascript")]
namespace YourNamespace.CustomControlsNamespace
{
public CustomControl()
{
...
}
...
protected override OnPreRender(EventArgs e)
{
...
Type type = typeof(CustomControl);
string scriptUrl = Page.ClientScript.GetWebResourceUrl(type, "Acuity.Web.UI.WebControls.Scripts.accdaterange.js");
string key = "yourKey";
if (!Page.ClientScript.IsClientScriptIncludeRegistered(type, key))
{
control.Page.ClientScript.RegisterClientScriptInclude(type, key, scriptUrl);
}
...
}
...
}
You could also reference an external script in your custom control.
namespace YourNamespace.CustomControlsNamespace
{
public CustomControl()
{
...
}
...
protected override OnPreRender(EventArgs e)
{
...
Type type = typeof(CustomControl);
string scriptUrl = Page.ResolveClientUrl("~/yourScriptsFolder/yourExternalScript.js");
string key = "yourKey";
if (!Page.ClientScript.IsClientScriptIncludeRegistered(type, key))
{
control.Page.ClientScript.RegisterClientScriptInclude(type, key, scriptUrl);
}
...
}
...
}
Depends how you want to package it. The advantage of the embedded resource is that you're guaranteed that this script is always with the assembly that your custom control is in. You will most likely have to add some inline JavaScript to wireup your custom control. Try and do this as little as possible. You can do this using Page.ClientScript.RegisterClientScriptBlock(...)
. You also want to avoid hard-coding what the auto-generated client Ids are in your scripts. They should be passed in as parameters to client-side objects.
Also, once all looks good, you should compress/minify you external JavaScript files. I use YuiCompressor from Yahoo! but there are several others out there.
You should also invest some time into looking at using a JavaScript framework such as jQuery to do a lot of the grunt work. That's it for now. I might add some more later, but these are my words of wisdom for now.
Best Answer
Panel doesn't work the same way as TextBox (and most controls). There is no Render method, so the override is never called. Panel uses RenderBeginTag and RenderEndTag. You may want to use the AddAttributesToRender method too.