Suppose you a have a text box in an ASP.Net page. This text box needs to contains calculated values from other inputs that will be received from the user (other text boxes). The user can not change the text in the TextBox
directly.
How do you implement that? By using the ReadOnly
property in the TextBox
.
That's what one of the programmers on my team did a long time a go. Recently a bug in his page was found: After a post-back the values in the read-only text box have been reset, which is a problem because the JavaScript code in the page rely on the values of this text box to perform calculations, which after the post back have been incorrect.
One of my team mates asked me to help him to solve this bug. He already set on it for hours , debugging each JavaScript event and each line of code in the CS file in order to find where the value is being reset. He found that all through the client script flow the value of the text box stays correct, but at the first line in the server code the value has been already reset. We debugged the code together for a couple of hours but couldn't found the problem.
In the following weekend I thought about it all the time and then it came to me: The TextBox
is ReadOnly
!!!
MSDN explains it: The Text value of a TextBox
control with the ReadOnly
property set to true
is sent to the server when a post-back occurs, but the server does no processing for a read-only text box. This prevents a malicious user from changing a Text
value that is read-only. The value of the Text
property is preserved in the view state between post-backs unless modified by server-side code.
It doesn't get any simple then that. In general the value of a ReadOnly
text box can only be changed by server code.
How to solve the bug?
There are two ways:
- Recalculate the value of the
TextBox
in the server after a post-back and set it there. The problem in this is duplicated code. The same calculation is performed in a script and in the server code, so any future modification in the calculations need to be done in both. - Use the readOnly attribute of the input tag. This prevents the user from manually change the value of the
TextBox
and the value is still sent to the server. To do this you need to run the following code:
TextBox1.Attributes.Add(“readOnly“, “true“);
The problem with this is the value can be changed by the client. By changing the request (Using Fiddler) or running a javascript command through the address bar in the browser.
Here's a code example for the problem and the second solution:
Html code:
<%@ 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>
<title>Untitled Page</title>
<script language=“javascript“ type=“text/javascript“>
function changeData() {
var readOnlyData = document.getElementById(“<%=ReadOnlyData.ClientID%>“);
readOnlyData.value = parseInt(readOnlyData.value) + 1;
}
</script>
</head>
<body>
<form id=“form1“ runat=“server“>
<div>
<asp:TextBox ID=“ReadOnlyData“ runat=“server“ Text=“0“></asp:TextBox>
<input id=“ChangeData“ type=“button“ value=“Change“ onclick=“changeData()“ />
<asp:Button ID=“DoPostBack“ runat=“server“ Text=“PostBack“ />
</div>
</form>
</body>
</html>
Code behind:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// The problem : The text box is set as read only
//ReadOnlyData.ReadOnly = true;
// The solution : We replaced it with the readOnly javascript attribute
ReadOnlyData.Attributes.Add(“readOnly“, “true“);
}
}
}
You can see:
- TextBox which contains the value.
- Input button which changes the value of the text box incrementing it's value by one.
- ASP.Net button which simply perform a post-back.
Try to run it with and without the commented line which sets the ReadOnly
to true, and see how the value of the textbox is being reset after a post-back.
The lesson that I've learned after solving this bug: Sometimes the most strange and complex problems have a simple solution.
P.S.: I think using a relying on a calculating value from the client is a bad practice. The page that is presented to the user is the UI. It is not suppose to perform calculations. This is the server code job. So if I had to rewrite the page I would choose the first solution.