I am running into an issue where I have multiple forms with a number of controls on them (20-40). The problem is that when I handle the postback, I need to put their values into variables and if they are not asp.net server controls (i.e select, input, etc...) I sometimes need to make sure they even exist. So, if I have a plain html checkbox, which is unchecked, it will not be posted to the server and you need to check for its existence, before being able to get its value. After that I need to pass them into a method to save to the database. The method handles all my crud and business validation. Setting this up is tedious at best and very time consuming. What are people doing to handle this? I am using ASP.Net 4.0 Web forms and VB.Net. One thought was to pass the http context into the method and let the code in the method look for the values. Still, doesn't seem that good of a solution. Any advice would really be appreciated, since I know I am not the only one who has run into this problem. Thanks in advance.
Wade
For large forms, you can:
create javascript object on client, convert it to JSON string, put JSON string to ASP .NET control Hidden or invisible textarea;
submit form and deserialize JSON to object on server.
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="Scripts/jquery.validation.net.webforms.min.js" type="text/javascript"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:HiddenField runat="server" ID="Hidden1" />
<input type="checkbox" id="CheckBox1" checked />
<input type="checkbox" id="CheckBox2" />
<input type="text" id="text1" name="text1" value=""/>
<asp:Button runat="server" Text="Button" ID="Button1" OnClientClick="createJSON()" OnClick="Button1_Click" />
<script type="text/javascript">
function createJSON() {
$('#Hidden1').val(JSON.stringify({
field1: $('#CheckBox1').is(':checked'),
field2: $('#CheckBox2').is(':checked'),
field3: $('#text1').val()
}));
}
$(document).ready(function () {
$("#form1").validate({
onsubmit: false,
rules: {
text1: {
required: true,
digits: true
}
}
});
$("#Button1").click(function (evt) {
var isValid = $("#form1").valid();
if (!isValid) evt.preventDefault();
});
});
</script>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web.Script.Serialization;
public class myClass
{
public bool field1;
public bool field2;
public string field3;
}
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
var result = (myClass)(new JavaScriptSerializer()).Deserialize(Hidden1.Value, typeof(myClass));
}
}
Install validation:
PM> Install-Package JQuery.Validation
PM> Install-Package JQuery.Validation.WebForms
From the moment that some controls they not post back any value, like the check box if is not checked you have two options.
Know all your controls before and after the post back / or
Create with javascript all the names of the controls and post them back in a hidden field.
Now, on the post back you have all the posted values on the HttpContext.Current.Request.Form where you can read them all.
On the client side you can use simple javascript or jQuery to create the list of all input controls and send them on a hidden input. Here is an example:
var inputs, index, cNames = "";
inputs = document.getElementsByTagName('input');
for (index = 0; index < inputs.length; ++index) {
cNames += "&" + inputs[index].name;
}
document.getElementById("AllControls").value = cNames;
or the same with jQuery
var cAllNames = "";
$('input').each(function() {
cAllNames += "&" + $(this).attr("name");
});
jQuery('#AllControls').val(cAllNames);
and on your page you have a hidden control like
<input type="hidden" name="AllControls" id="AllControls" />
and on the post you have all the names of your controls in a line like:
AllControls=&__VIEWSTATE&__EVENTVALIDATION&cbBoxTest&AllControls&Button1
There you can split that string, each name is seperated with the &, and there you can see what is used, what is not. You can either pass additional informations about that controls - the same way I send the name of each.
For each web form create a model class that has public properties with all the fields that may be on the form. Create name, validation and default value attributes and apply them to the properties. During the postback, find out what the model class is needed, create an instance, iterate its public properties and apply: if post has the field with property name - validate the value and apply to the field, if not - apply the default. Something like (i'll use c# but I guess it's clear anyway)
MyCrudModelForSomeForm {
[MyWebFormField(Default = 42, Type = typeof(int), Name = "txtAmount")]
public int SomeInt { get; set; }
[MyWebFormField(Default = "Hello", Type = typeof(string), Name = "txtMessage", Validate = "$[A-Z]{6}^")]
public string SomeString { get; set; }
[MyWebFormField(Default = "Zimbabwe", Type = typeof(string), Name = "txtCountryChoice")]
public string SomeOtherString { get; set; }
}
That's basically the custom implementation of M(odel) from MVC concept.
The ASP.NET Webforms arch does not support multiple forms
If you have created a page that has multiple forms then i would suggest use jQuery and Post Individual form to the respected handler to process the request
This will be simple and elegant
Related
I have a fortify result saying that the following line needs a secret to prevent CSRF
<form id ="form1test1" runat="server">
I have a random GUID being generated on Page Load, I want to compare it, when the form posts.
I have seen in classic asp putting the token in the action as a query string
I am trying:
<form "form1test1" runat="server"
action='<%# string.Concat(Eval("login.aspx/?Token="),"",Eval(Session["Token"].ToString()))%> '> >
Best I get is a print out of the text but not the values, not doing this in the code behind does not fix the finding in fortify
trying for something like
<form "form1test1" runat="server" action="login.aspx/?Token=12345DEF">
A better way to prevent CSRF attacks (it's working on my projects), is to implement it in your master pages, like this:
Add new Class that will handle the CSRF Validations for you:
public class CsrfHandler
{
public static void Validate(Page page, HiddenField forgeryToken)
{
if (!page.IsPostBack)
{
Guid antiforgeryToken = Guid.NewGuid();
page.Session["AntiforgeryToken"] = antiforgeryToken;
antiforgery.Value = antiforgeryToken.ToString();
}
else
{
Guid stored = (Guid)page.Session["AntiforgeryToken"];
Guid sent = new Guid(antiforgery.Value);
if (sent != stored)
{
// you can throw an exception, in my case I'm just logging the user out
page.Session.Abandon();
page.Response.Redirect("~/Default.aspx");
}
}
}
}
Then implement this in your master pages:
MyMasterPage.Master.cs:
protected void Page_Load(object sender, EventArgs e)
{
CsrfHandler.Validate(this.Page, forgeryToken);
...
}
MyMaster.Master:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:HiddenField ID="forgeryToken" runat="server"/>
...
</form>
Hope you'll find this useful.
On my current project I need to add a functionality that allows the user to view a thumbnail of their uploaded PDF. I've found a handy component that achieves this (the basic version is free, but it's enough for my current needs). Anyways, the control is pretty outdated (2010), therefore there doesn't seem to be MVC support. On the demos they depict usage of the control as such:
The View's Markup:
<form method="post" runat="server" enctype="multipart/form-data">
<asp:Panel ID="thumbnailsPanel" runat="server" />
</form>
The thumbnail control is instantiated via code, the byte array which represents the thumbnail is passed to the control and the control is added to thumbnailsPanel
<script runat="server">
protected void DisplayThumbs_Click( object sender, System.EventArgs e )
{
Thumbnail thumbnail = new Thumbnail();
thumbnail.SessionKey = sessionID;
thumbnail.Index = i;
thumbnailsPanel.Controls.Add( thumbnail );
}
</script>
Given that I can't declare a Thumbnail control in my razor view, how would I used this control in MVC? I've spent a few hours trying to make this control MVC friendly to no avail, the best I've come up with is to include a .ASPX view (not.cshtml) in my project and render the Thumbnail control on that view. Obviously this is not desirable.
So how would you go about using a ASPX server controls in MVC? Is the idea a bad one altogether and should not be practised?
I worked around it in a project of mine by reimplementing the control as a HtmlHelper. Assuming the control isn't too complicated then it should work for you too. Do this:
Dump the Control's source using Reflector
Massage the source so it actually compiles (as source from Reflector doesn't usually compile straight away)
Identify what state the control has. Convert the state from member properties into members of its own new ViewModel class.
Find the Render method and convert it to a HtmlHelper that uses ViewContext.Writer
For example:
public class FooControl : Control {
public String Message { get; set; }
public override void Render(HtmlTextWriter wtr) {
wtr.WriteLine("<p>");
wtr.WriteLine( message );
wtr.WriteLine("</p>");
}
}
Becomes this:
public class FooViewModel {
public String Message { get; set; }
}
// This method should exist in a static Extensions class for HtmlHelper
public static void Foo(this HtmlHelper html, FooViewModel model) {
HtmlTextWriter wtr = html.ViewContext.Writer;
wtr.WriteLine("<p>");
wtr.WriteLine( model.Message );
wtr.WriteLine("</p>");
}
Im working on an e-commerce platform which supports multiple stores on different domains. The backend is complete (for now) and I'm now starting the front-end part. Since the platform supports multiple stores I need some kind of templating system and I haven't quite figured out what's the best way to do this.
This are my requirements:
I don't want to rebuild my solution to add a new template
Templates are stored in /Templates/TemplateDirectoryName
I want to be able to use (predefined) usercontrols inside the templates.
I use URL routing and only have 1 Default.aspx page which controls which page template and additional code needs to be loaded.
This is what I came up with so far:
In my template directory i have templates created with master pages (A homepage master file, a default master file, and sub-master files referencing the default master file...for product detail, browse, search etc)
My Default.aspx page picks the right template based on routing values
While this way works I don't think it's very practical but the more I think about it the more I come to the conclusion that there are not so many other options to go around this. I think this is what I want to ask: Is there a way to use usercontrols in a template and have the template completely seperated from the application so users can create templates without having to worry about the site's namespace and structure?
Kind regards,
Mark
Since you are referencing a folder for templates, wouldn't it be possible to just change the .aspx files in the folder and asp.net shall pick up the template based on the url path that you've mentioned? I think that is possible in asp.net.
Also, Frameworks like DotNetNuke, Sharepoint, Joomla etc. have the similar concept. You can avail their features.
My proposed solution is below. It has a few constraints, like all master pages need to implement the same set of placeholder controls (not surprising). Take a look and let me know what you think.
I setup my folder structure like this:
Website -> Templates -> TemplateFolder (named same as the template)
Website -> Templates -> UserControls (User controls are stored in a non-template specific folder)
I defined a simple Template configuration class which we can store/save/load a basic template deffinition:
public class Template
{
public string TemplateName { get; set; }
public string UserControlName { get; set; }
public string MasterPageName { get; set; }
public string TemplateFolder
{
get
{
return GetTemplateFolder(TemplateName);
}
}
public string TemplateConfigFile { get { return GetTemplateConfigFile(TemplateName); } }
private static string GetTemplateFolder(string name)
{
return HttpContext.Current.Server.MapPath("~/Templates/" + name + "/");
}
private static string GetTemplateConfigFile(string name)
{
return GetTemplateFolder(name) + "/" + name + ".config";
}
public Template()
{
}
public void Save()
{
XmlSerializer xs = new XmlSerializer(typeof(Template));
if (!Directory.Exists(TemplateFolder)) Directory.CreateDirectory(TemplateFolder);
using (FileStream fs = File.OpenWrite(TemplateConfigFile))
{
xs.Serialize(fs, this);
}
}
public static Template Load(string name)
{
if(!File.Exists(GetTemplateConfigFile(name))) return null;
XmlSerializer xs = new XmlSerializer(typeof(Template));
using (FileStream fs = File.OpenRead(GetTemplateConfigFile(name)))
{
Template t = (Template)xs.Deserialize(fs);
return t;
}
}
}
You can build some bare xml code to get started with by running the code below:
Template t1 = new Template() { TemplateName = "Template1", MasterPageName = "Child1.master", UserControlName = "uc1.ascx" };
Template t2 = new Template() { TemplateName = "Template2", MasterPageName = "Child2.master", UserControlName = "uc2.ascx" };
t1.Save();
t2.Save();
I created a basic master page. This page will probably never be used except to give your default page the basic placeholders. All of your master pages should have the same set of placeholders as your base one so that your pages can use them all interchangably. Notice I left a placeholder for our user control.
<%# Master Language="C#" AutoEventWireup="true" CodeFile="BaseMaster.master.cs" Inherits="Templates_Masters_BaseMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder id="cphHeader" runat="server">
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder id="cpUserControl" runat="server">
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder id="cphFooter" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
Now I create a basic aspx web page that uses the above master page.
<%# Page Title="" Language="C#" MasterPageFile="~/Templates/Masters/BaseMaster.master" AutoEventWireup="true" CodeFile="DefaultTemplated.aspx.cs" Inherits="DefaultTemplated" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="cphHeader" Runat="Server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="cpUserControl" Runat="Server">
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="cphFooter" Runat="Server">
</asp:Content>
In the codebehind we'll setup the basic templating. This sets the masterpage and adds a pre-defined user control to the content placeholder for user controls. If you wanted you could just make a panel or something and add it to that fixed control, but I thought you might appreciate seeing how to make it work with masterpages.
public partial class DefaultTemplated : System.Web.UI.Page
{
private Template PageTemplate
{
get
{
if (_tLoaded == null)
{
string template = Request.QueryString["template"];
if (string.IsNullOrEmpty(template)) return null;
Template t = Template.Load(template);
_tLoaded = t;
}
return _tLoaded;
}
}
private Template _tLoaded = null;
protected void Page_Load(object sender, EventArgs e)
{
if (PageTemplate != null)
{
//cpUserControl is the name of my usercontrol placeholder
((ContentPlaceHolder)Page.Form.FindControl("cpUserControl")).Controls.Add(
Page.LoadControl("~/Templates/UserControls/" + PageTemplate.UserControlName));
}
}
protected void Page_PreInit(object sender, EventArgs e)
{
if (PageTemplate == null) return;
this.MasterPageFile = "~/Templates/" + PageTemplate.TemplateName + "/" + PageTemplate.MasterPageName;
}
}
If you had a template named "Template1" you could use it by calling "Default.aspx?template=Template1". Since you are using URL rewriting you would use the rewriting to pass the template name as a parameter to the page.
Another option that could be combined with the above would be the use of Page.ParseControl. Using this you could store your raw asp.net designer code (designer only) in a database or raw text file. Then you could instantiate it load it like this:
//where pnl1 is a Panel on the page. Page.ParseControl just returns a control object, so use it anywhere.
pnl1.Controls.Add(Page.ParseControl("raw asp.net designer code here."));
One great thing about this is that nested controls work great too.
I don't know if I understand correctly:
If you don't want to rebuild, then I can tell a CMS concept is best suitable for you.
You can store your templates as an HTML in database and from DB you can fetch it back,
You can give an admin functionality with editor to edit your template online also.
This is my ascx Code:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="Demo.ascx.cs"
Inherits="Demo" %>
<asp:HiddenField ID="hidden" runat="server" Value="" />
And the aspx:
<%# Register TagName="Hidden" TagPrefix="CRS" Src="~/Demo.ascx" %>
<div>
<CRS:Hidden ID="hid" runat="server" />
</div>
Now How to access Hidden variable ID From ascx page to this cs page backend
Do you mean the actual ID? or the Value within the hidden field?
You can access the value using the FindControl method
HiddenField hf = (HiddenField)this.hid.FindControl("hidden");
string theValue = hf.Value;
Not sure if this is exactly what you are looking for.
Alternatively, you can declare some public properties in the UserControl in which you can access directly
In the ascx code:
public string theValue { get; set; }
In the aspx code:
string theValue = this.hid.theValue;
To access the HiddenField inside the UserControl from the asp.net web page you will need to wire up something called a Public Property.
This code should be added to the UserControl ascx.cs code behind:
public string Value
{
get { return hidden.Value; }
set { hidden.Value = value; }
}
You could then write code like this in your asp.net page:
string SomeHiddenValue = hid.Value;
hid.Value = "Its a secret!";
Note: I haven't compiled this so I am not sure if the public property name of Value will compile. I am also not sure if the second value in set { hidden.Value = value; } needs capitalising. Try changing these two values if you encounter problems.
Lets say I have a class that stores user information complete with getters and setters, and it is populated with data from an XML file. How would I iterate over all of the instances of that class like you would do with java beans and tag libraries?
For outputting formatted HTML, you have a few choices. What I would probably do is make a property on the code-behind that accesses the collection of objects you want to iterate over. Then, I'd write the logic for iterating and formatting them on the .aspx page itself. For example, the .aspx page:
[snip]
<body>
<form id="form1" runat="server">
<% Somethings.ForEach(s => { %>
<h1><%=s.Name %></h1>
<h2><%=s.Id %></h2>
<% }); %>
</form>
</body>
</html>
And then the code-behind:
[snip]
public partial class _Default : System.Web.UI.Page
{
protected List<Something> Somethings { get; private set; }
protected void Page_Load(object sender, EventArgs e)
{
Somethings = GetSomethings(); // Or whatever populates the collection
}
[snip]
You could also look at using a repeater control and set the DataSource to your collection. It's pretty much the same idea as the code above, but I think this way is clearer (in my opinion).
This assumes you can acquire all instances of your class and add them to a Generic List.
List<YourClass> myObjects = SomeMagicMethodThatGetsAllInstancesOfThatClassAndAddsThemtoTheCollection();
foreach (YourClass instance in myObjects)
{
Response.Write(instance.PropertyName.ToString();
}
If you don't want to specify each property name you could use Reflection, (see PropertyInfo) and do it that way. Again, not sure if this is what your intent was.