Assume you have a page in ASP.NET where it makes sense to use JavaScript/jQuery to modify the DOM with values that will eventually be read by the server on submit. For example, you have a form that allows end users to add/remove objects (like dependents on a health insurance form or assets on a loan application).
What are some ways to ensure that these items are detected and retrieved by the server once the information is submitted?
I've tried a few things that work, but none of them seem perfect so I wanted to see what the community had to offer.
Also, I'm not looking for suggestions that avoid dynamic DOM elements (like use a Wizard, etc.). I'm specifically trying to improve my technique with dynamically created DOM elements.
There are two ways to do something like this:
First: post only the information on submit. As long as you add both a name and an id attribute to every element in your DOM, every element is represented in Request.Form, so you can easily iterate through this collection.
I tend to have a naming convention like insurance_row_1, insurance_row_2 and so forth. You can find all rows like Request.Form.AllKeys.Where(k=>k.StartsWith("insurance_row_")).
When you want to save every action on the server:
Maintain a state container in javascript, that holds information in some dictionary-like control, where you put every action that has been performed by your application, and a state whether the server has processed them. Something like:
var stateContainer = [
{ 'put-insurance-row-1', false },
{ 'delete-insurance-row-1', false }
];
Do an AJAX request to the server to perform such an action, and use the state-container's key to track whether the request succeeded or failed. Set the state to 'true' when the request has been successfully submitted. Make sure to do this in order in which you receive the events, so send them one-by-one to the server to ensure that you keep a valid state.
You can (try) to prevent closing the browser if some states aren't persisted yet.
why are you using client side code to do this? i would go the route of doing a postback and adding a control from server side code. the built in ajax updatepanel should handle this very quickly.
Related
As we know, ASP.NET WebForms will generate a Unique ID (as well as name) to a control to prevent collisions in the control heirarchy. Let's say we have a TextBox control with an assigned ID of "MyTextBox" in the markup. If this textbox is on a page with a Master Page then the TextBox control will be given a Unique ID of "ctl00$MainContent$MyTextBox" or something similar.
What I want to know is, for a given page, is it possible to know what the prefix WILL BE? In my above example I would like to know all controls I create on that page will be assigned with a prefix of "ctl00$MainContent$". I have examined the Page object and I cannot find an easy way to extract this information. Note: inspecting already existing controls on the page (like the TextBox) isn't an option. I simply need to know, at run time, what the prefix would be.
-- EDIT: Why do I need to do this? --
Ultimately I am trying to solve the problem that this post illustrates:
ASP.NET 4.5 TryUpdateModel not picking Form values in WebForm using Master-Page
I'm using the ModelBinding features introduced in ASP.NET 4.5. Problem is, as the above post points out, is that the name value collection found in the form will NOT match up with your model's properties. The built-in FormValueProvider expects a one-to-one match with the form key (name) and the model's properties. Unfortuantely, the form's keys will have the typical "ctl00$MainContent$" prefix to the names.
I have a semi-working solution where I created a custom IValueProvider that compares the end of the form key with the model's property. This works 95% of the time, but there's always a chance of multiple hits.
Ideally, and this is what I'm trying to figure out, if I could determine WHAT the prefix is I can then prefix that the IValueProvider's passed in key, look for that in the form and find the exact match.
So that is why I'm wondering if there's any way to know what the prefix should be for a given page.
The real answer is to simply code in such a way that you never have to know this information. that might not always be easy - but that's quite much what we do. You can certainly in code behind get/grab the "id" of the given button.
so for example, I become VERY tired of having to wire up a little toast message all over the place. So, I put in a little js routine (in the master page).
But I did need the client ID of a given control.
Well, the code behind needed (wants) to place the toast message next to whatever I clicked on.
So my server side "toast" message caller of course will after the server side code is done does the common "script" inject that will run when the page finally makes it final trip back down to the browser, displays the page, and then of course runs that script I injected.
So, my server side code does this:
MyToast2(Me, btnUpdate.ClientID.ToString, "Update ok!", "Settings changed")
So note how I get/grab/pass the "ID" of the control that the server is going to render. You can use ClientID to get the the final "ID" used for that control in code behind.
So, that btnUpdate is just a simple button placed on the web form. But who cares what super ugly "ID" the server assigns. i just need the "id" of the control so the JavaScript client side can pick up that control - and thus know/get/have the position of the control, and thus I get this result:
Or if I am some place else - again I can call that js routine - and that routine needs the current control. so might have this:
So, I can now just call a routine and pop up a message - not have to write any new js code for the gallzion notices and little pops I have all over the place.
so the little javaScript routine of course does this:
function toastcallm(cntrol, h, t, d) {
var cmd = $('#' + cntrol);
var mypos = cmd.position();
bla bla bla
But only important was that I get/determine and pass the used server "client" id to that routine - I don't really care what it is , or how to see, or how to list them out. I suppose a better jQuery selector or using wild card might work - but I really don't want to know the control ahead of time - but only that I can get the clientID used when I need it.
I simply write code that assumes somewhere along the way when I need such a client id, I simply get it and use it.
So, on the server side? Well, we always build and write code based on the control ID, but you want to get your hands on the actual id? Then you can use in the server code behind:
btnUpdate.ClientID.ToString
(ie: somecontrol.ClientID).
I am totally new to classes and OOP, so please bear with me.
I am creating a large scale web app which I'm trying to keep tidy by creating my own classes.
For instance I have a Public Class Product which has several properties. One way I am using it is on page load a product ID is assigned to the ID property which in turn gets the details for that product and assigns the various data to the other properties. So within my code I can used for example product.price or product.description and get the appropriate values. This has worked fine, but I found that because the class was initiated on page load it was getting the data from the DB each time that the page refreshed. I stopped this by using an If Not IsPostback to initiate the class. This meant that the data was pulled in only on the initial page load. So far so good.
I then needed to compare a value in a textbox with a property of the product. I have a textchanged event with
If textbox1.Text <> product.description Then....
but here I get a wavy line under product.description and VS2010 is saying that the object is not defined. Its Dim'd in the page.load so I moved the Dim statement outside the page class so that it will be accessible to all events on the page.
The dim statement is Dim product as New product
In my not ispostback chunk of code I have for example product.ID = 1 which will get all the product properties for product 1
The wavy line has gone but when I run the page all works fine on page load. Data is displayed so my product class is working fine. As soon as I make a change in textbox1 and the event triggers product.description is nothing. It got reinitalised.
How do I stop this from happening...
Your "Product" is not persisted between postbacks.
Only control objects in aspx page are persisted/restored automatically.
To remedy this there are multiple approaches.
If Product is loaded via setting "Product.id=1" then what I woudl do is have a hiddenfield that receives the value of the product.id during prerender event (to save it in the page) and in an init event I would restore the "Product.id=hiddenfield.value" but only when it is a postback to reload your object.
EDIT
Thanks for picking my answer. I'll elaborate a little on the various ways to deal with this and why I suggested my answer.
Store Key in HiddenField Reload from DB:
PROS: Product is always Fresh/Correct/Current values. Corresponding to the database. Databases are very efficient to return a record based on a primary key. Very little data is sent to and posted back from the client browser. Low complexity. Each page opened by the client is safely isolated.
CONS: Multiple database transactions. If the DB is already strained or extremely massive you may need to consider even the smallest efficiency gain, but this is not common or likely on a primary key based record
Session State (store entire object):
PROS: Lowest time to "load" object since it's available in memory already once loaded. Less DB Transactions. No data piggy backed to the client and back again.
CONS: Object can become "out-of-date" if altered in the DB. Users who open multiple pages of your application can end up getting the wrong object if both require a different "Product", so instead to be totally safe you need a more complex structure in place to store more then one product or store them based on some kind of key (such as the product ID). Server Memory is used, if serving thousands of users or your product data is large it can become an issue, especially if you do this in many pages with many objects.
Serialization (store the entire object in the page in a field, similar to event state):
PROS: Once loaded, the Database is accessed only once for a specific product, then the product is held, in it's entirety inside the page, it is recreated by the server from the data in the field or via viewstate. Each page opened by the client is safely isolated. Is fairly easy to implement storing in ViewState of the Page.
CONS: Object can become "out-of-date" if altered in the DB. ALLOT more data is added to your page responce and the users next page request. Is more complex to implement because the object needs to be designed to be serialized correctly. Complex objects require allot of manual code to be serialized successfully.
again, there are many other ways to deal with this, such as storing items in a synclocked dictionary style object global to the application, but is considerablby more and more complex as you go.
This is likely the standard ASP.NET page life cycle problem.
After you initialize the page, it gets sent to the user's browser. When the user clicks on something, the browser sends a postback request back to your application. The view state allows the textbox1 object to remember what was in its Text property. However, your Page_Load ran from scratch, and, yes, everything including your product object got recreated from scratch.
If you want your product object to "remember" what it knew before the postback, you'll have to remind it. One way would be to store the initialized value in Session state, and then refresh your product object during the postback section of the Page_Load method.
Every time you do a postback, you're working with a new instance of your page class. The prior copy of your class was thrown away and probably disposed before your browser even rendered the page to the screen.
If you want to persist a value across http requests (of which postbacks are just one type), then you need to put it somewhere like the Session.
I have following ajax call
open: function () {
$(this).load("MyBox.aspx?sec=L&levId=" + RowId);
}
so people can see the querystring, so the user can copy url and paste it to browser but how can i block that? I dont want it to shown from browser. How I can do that? I m using asp.net and jquery.
load function issues a GET request. Instead of that, you may use a jQuery POST call and get the data. Users can't get the result by pasting it in browser and hit enter (which is GET request)
var thatObject=$(this);
$.post("MyBox.aspx?sec=L&levId=" + RowId,function(response){
thatObject.html(response);
})
In the server page, you can read the values posted by checking the Request.Form collection (instead of Request.QueryString).
from msdn
The Form collection retrieves the values of form elements posted to
the HTTP request body, with a form using the POST method.
You can determine whether the call is a GET call or POST call by inspecting the Request.RequestType property value. This way you can avoid people issuing GET request to this method and getting the response.
But remember that, there are tools/browser addons which does the POST request from browser.
Also if the data is for authorized users, you may check the user is authorized to access it in the server page(MYbox.aspx) before returning the content.
You can't. You can never trust any code running on the client. If you need to hide data on the client, you should create a server based session and then put a session token in an encrypted cookie.
From wikipedia and W3C
Some methods (for example, HEAD, GET, OPTIONS and TRACE) are defined as safe, which means they are intended only for information retrieval and should not change the state of the server. In other words, they should not have side effects, beyond relatively harmless effects such as logging, caching, the serving of banner advertisements or incrementing a web counter.
Making arbitrary GET requests without regard to the context of the application's state should therefore be considered safe.
By contrast, methods such as POST, PUT and DELETE are intended for actions that may cause side effect
If your get request changes the state of the server (which it most likely does based on your post), you are doing something wrong. What you're trying to do is impossible. You need to rethink your architecture.
Apologies if this is classic ASP 101, but its been so long since I did any ASP Im struggling to understand / track this error down. What makes it worse is Ive inherited this application and I cant ask the original author..
I have a shopping cart that includes an input checkbox and numerous other fields. When the form is processed and submitted it is run through some javascript and then if all is ok, redirected to another page. (Nothing unusual there). Firebug shows that at this point the value of the check box is different depending on its checked state.
When the form is submitted it goes to another page that iterates over the session.Contents() collection, and builds up a string that is sent to a 3rd party. Using fiddler, it appears that whilst the name of the checkBox is in this string, the value is always 'on'
From reading Google, I see that the session.Contents collection is all parameters that have been placed in the session / application. but a grep across all the files in the project directory doesnt turn up anywhere that the checkbox is added to the session.
So, is the cb there simply because it is on the form or used in javascript, or are there other ways of adding the variable into the session. (Grep on the name doesnt turn up any other instances).
And secondly, if the variable is in the session, no matter how it got there, why is it always set to "on". Im assuming that somehow it has been added to the session and set to On before the form is processed. But the checkbox defaults to unchecked, so Im confused!
Can anyone help explain this, or even suggest how I can track it down / fix it. (The obvious answer is to try to force it into the session with the correct value, but I'd like to know why it is misbehaving rather than just ignore it in case I meet something like this again!
Thanks
I am going to assume that you have already determined that the JavaScript is not modifying checkbox state priort to allowing the submission.
When your form is submitted, the fields that are submitted are in the Request.Form collection. When a checkbox is not checked, it is not part of the Request.Form collection. Therefore, there will be as many checkbox fields in your Request.Form collection as you had checked when submitting, and they will all have the value of their respective "value" property.
If you then add these to the Session.Contents collection, they persist until the session ends. If you never explicitly clear the Session.Contents collection, but submit the form more than once with different values, then the Session.Contents collection will continue to accrue more and more (checkbox_name, checkbox_value) pairs until such time as it contains a (checkbox_name, checkbox_value) pair for every checkbox on your form.
You may wish to write a function that clears each one of your form fields from the session, and call this either after processing a form, or before processing a form (whichever makes sense for your application). Alternatively, just use the Request.Form collection.
We heard a lot about the vulnerabilities of using QueryStrings and the possible attacks.
Aside from that, yesterday, an error irritated me so much that i just decide to stop using QueryStrings, i was passing something like:
Dim url As String = "pageName.aspx?type=3&st=34&am=87&m=9"
I tried to
Response.Write(url)
in the redirecting page, it printed the "type" as 3, then i tried it in the target page, it printed 3,0....i know this can be easily dealt with, but why? i mean why should i pass 3 and have to check for 3.0 in the next page's load to take my action accordingly???
So what should we use? what is the safest way to pass variables, parameters...etc to the next page?
You could use Cross-Page Postbacks.
Check also this article:
How to: Pass Values Between ASP.NET Web Pages
There are many options you can use, most of them requires you to build a strategy to pass variables between pages.
In most projects I use this strategy, I create a formVariables class to hold currently active items. it has properties which you will need to pass by querystring. and I store this class at session. and in my base page I read it from session. so in every page I get values over this object. the only negative thing about this method is to clean up items when you finished your work on it..
hope this helps.
I would sugest you avoid using Session to pass variables between pages as this breaks the stateless model of the web.
if you have just stored some values in session that relate to a certain page then the user uses their browsers back button to go back to the same page whcih should have a different state then you are not going to know about it.
It leads to the possibility of reading session values that are not relevant to the page the user is currently viewing - Which is potentially very confusing for the end user.
You will also run into issues with session expiration if you rely on it too much.
I personally try to avoid using session where possible in preference of hidden form values + query strings that can be read on postback + navigation.
The best / most secure way to pass info between pages is to use the session.
// On page 1:
this.Session["type"] = 3;
// On Page 2:
int type = (int)this.Session["type"];
You can store any kind of object in the session and it is stored on the server side, so the user can't manipulate it like a query string, viewstate, or hidden field
You said:
it printed 3,0....i know this can be easily dealt with, but why? i mean why should i pass 3 and have to check for 3.0
There's a difference between "3,0" (three comma oh) and "3.0" (three point oh). You also said that you were "passing something like".
In a query string, if you pass multiple values in the same key, they will be seperated with commas.
As all values are passed as strings there's no way that an int "3" is going to magically become decimal "3.0" unless you parse it as such when you request it.
I'd go back and double check what you are passing into your URL, if it ends up as something like:
pageName.aspx?type=3&st=34&am=87&m=9&type=0
Then when you read back
Request.QueryString["type"]
You'll get "3,0" back as the comma seperated list of values in that key.
First, in asp .net you can use several strategys to pass values between pages. You have viewstate too, however the viewstate store the value and the use is in different scenarios , you can use it too. Sessions instead, and of course by post in a form.
If your problem is the security, I recommended you to create 2 users for accesing the data. One user with read only access, this for accessing the pages ( Sql Inyection prevent ) and validate the data throw the querystring. And One with write access for your private zone.
Sorry, for my unreadeable English.
I like to use query string as I like users to be able to bookmark things like common searches and the like. E.g. if a page can work stand-alone then I like to it to be able to work stand-alone.
Using session/cross-page postbacks is cool if you needed to come from another page for the page you're on to make sense, but otherwise I generally find querystrings to be the better solution.
Just remember that query strings are unvalidated input and treat them with the caution you would treat any unvalidated input.
If you do proper security checks on each page load then the querystring is fine and most flexible IMHO.
They provide the most flexibility as the entry poitn to a page is not dependant on the sender as in some other options. You can call a page from any point within your own app or externally if needed via querystrings. They can also be bookmarked and manually modified for testing or direct manipulation.
Again the key is adding proper security and validation to the querystring, and not processing it blindly. Keep in mind that the seucirty goes beyond having edit or read access, depending on the data and user, they may not have access to the data with thos paranters at all, in cases where data is owned and private to specific users.
We have tried various methods, in an attempt to hide the querystring but in the end have gone back to it, as it is easier to do, debug, and manage.