Passing Objects via QueryString - asp.net

I have object A which in turn has a property of type Object B
Class A
property x as Object B
End Class
On my ASP.NET page when I select a gridview item which maps to an object of type A I serialize the object onto the QueryString and pass it to the next page.
However I run into problems if property x actually has some value as it looks like I exceed the QueryString capacity length of 4k (although I didn't think the objects were that large)
I have already considered the following approaches to do this
Session Variables
Approach not used as I have read that this is bad practice.
Using a unique key for the object and retrieving it on the next page.
Approach not used as the objects do not map to a single instance in a table, they arte composed of data from different databases.
So I guess my question is two fold
Is it worth using GKZip to compress the querystring further (is this possible??)
What other methods would people suggest to do this?

If displaying the url of the next page in the browser does not matter, you could use the context.items collection.
context.items.add("keyA", objectA)
server.transfer("nextPage.aspx")
Then on the next page:
public sub page_load(...)
dim objectA as A = ctype(context.items("keyA"), objectA)
dim objectB as B = objectA.B
end sub
One reason to use this is if you want the users to believe that the next page is really a part of the first page. To them, it only appears as if a PostBack has occurred.
Also, you don't really need a unique key using this approach if the only way to use "next page" is if you first came from "first page". The scope for the context items collections is specific to just this particular request.
I agree with the other posters who mentioned that serialized objects on the querystring is a much worse evil than using session state. If you do use session state, just remember to clear the key you use immediately after using it.

I don't understand why you wouldn't use session state but...
Option 1: Viewstate
Option 2: Form parameters instead of querystring
But also be aware that you do not get the same object back when you serialize/deserialize. You get a new object initialized with the values of the original that were serialized out. You're going to end up with two of the object.
EDIT: You can store values in viewstate using the same syntax as Session state
ViewState["key"] = val;
The value has to be serializeable though.

While storing objects in session might be considered bad practice, it's lightyears better than passing them via serialized querystrings.
Back in classic asp, storing objects in session was considered bad practice because you created thread-affinity, and you also limited your ability to scale the site by adding other web servers. This is no longer a problem with asp.net (as long as you use an external stateserver).
There are other reasons to avoid session variables, but in your case I think that's the way to go.
Another option is to combine the 2 pages that need access to this object into one page, using panels to hide and display the needed "sub-pages" and use viewstate to store the object.

I don't think passing it in the query string, or storing it in the session, is a good idea.
You need one of the following:
a) A caching layer. Something like Microsoft Velocity would work, but I doubt you need something on that scale.
b) Put the keys to each object in the databases that you need in the query string and retrieve them the next time around. (E.g. myurl.com/mypage.aspx?db1objectkey=123&db2objectkey=345&db3objectkey=456)

Using session state seems like the most practical way to do this, its exactly what its designed for.

Cache is probably not the answer here either. As Telos mentioned, I'm not sure why you're not considering session.
If you have a page that depends on this data being available, then you just throw a guard clause in the page load...
public void Page_Load()
{
if(!IsPostBack)
{
const string key = "FunkyObject";
if(Session[key] == null)
Response.Redirect("firstStep.aspx");
var obj = (FunkyObject)Session[key];
DoSomething(obj);
}
}
If session is absolutely out of the quesiton, then you'll have to re-materialize this object on the other page. Just send the unique identifier in the querystring so you can pull it back again.

Session isn't always available. For instance when XSS (cross-site-scripting) security settings on IE prevent the storage of third-party cookies. If your site is being called within an IFrame from a site that's not your DNS domain, your cookies are going to be blocked by default. No cookies = no session.
Another example is where you have to pass control to another website that will make the callback to your site as a pure URL, not a post. In this case you have to store your session parameters in a querystring parameter, something that's tough to do given the 4k size constraint and URL encoding, not to mention encryption, etc.
The issue is that most of the built-in serialisation methods are pretty verbose, thus one has to resort to a roll-your-own method, probably using reflection.
Another reason for not using sessions is simply to give a better user experience; sessions get cleared after N minutes and when the server restarts. OK, in this case a viewstate is preferable, but sometimes it's not possible to use a form. OK, one could rely on JavaScript to do a postback, but again, that's not always possible.
These are the problems I'm currently coding around.

Here is what I do:
Page1.aspx - Add a public property of an instance of my object. Add a button (Button1) with the PostBackURL property set to ~/Page2.aspx
Private _RP as ReportParameters
Public ReadOnly Property ReportParams() as ReportParameters
Get
Return _RP
End Get
End Property
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
_RP = New ReportParameters
_RP.Name = "Report 1"
_RP.Param = "42"
End Sub
Now, on the second page, Page2.aspx add the following to the Markup at the top of the page under the first directive:
<%# PreviousPageType VirtualPath="~/Default.aspx" %>
Then for the Page_Load in the code behind for Page2.aspx, add the following
If Not Page.PreviousPage is Nothing Then
Response.write (PreviousPage.ReportParams.Name & " " & PreviousPage.ReportParams.Param)
End If

Faced with a similar situation what I did, is to XML serialize the object and pass it around as query string parameter. The difficulty with this approach was that despite encoding, the receiving form throws exception saying "potentially dangerous request...". The way I got around was to encrypt the serialized object and then encode to pass it around as query string parameter. Which in turn made the query string tamper proof (bonus wandering into the HMAC territory)!
FormA XML serializes an object > encrypts the serialized string > encode > pass as query string to FormB FormB decrypts the query parameter value (as request.querystring decodes also) > deserialize the resulting XML string to object using XmlSerializer.
I can share my VB.NET code upon request to howIdidit-at-applecart-dot-net

Related

Whats the best way to handle variables in multiuser asp.net site

I've created an asp.net page for work that allows customers to look up users in AD, and then request to have them added as delegates to Rightfax numbers, which can also be searched for. Rightnow I'm storing public variables used by the back end and front end of the project in a CurrentSession class inherited by the pages thats supposed to be unique to each user, but I'm still seeing occasional issues where variables will 'bleed' from one session to another. Sometimes I'll go to the page and the list of AD users is already populated with users from another session\user.
I'm wondering what the best method is for storing variables in this scenario. Should I be using cookies rather than a current session class? Are there any good guides/tutorials that go over variable management for asp.net pages? I'm typically a desktop developer so I'm not particularly familiar with this kind of issue.
the whole jump to the web, and that of session managment, or varible managment is a huge topic. And it is often a challenge, since the concpets are very different then desktop.
So, I mean, when a user clicks a button, the web page is posted up to the server. The page is found, loaded, variables start from scratch. Code behind runs, page is sent back down to client AND THE PAGE SERVER SIDE IS TOSSED out!
So, the above is a new challenge, due to that so called "state-less" nature of web pages.
As for session bleeing to other users? Hum, that should not occur. However, session() can be VERY fragle. Due to time outs, due to some execution error in code, the app pool can and will often re-start.
So, I strong recommend that you run the script and turn on SQL servre based sesison management. Once done, then session() becomes bullet proof - and always works.
As for values bleeding to other users? No way - I not seen that.
(of course, you did not mention or note what authentication provider you are using (or even if users have to logon).
However, while I OFTEN use session, and even to pass values from one page to the next? (absolute hate parameters in the URL - messy and often security risk if things like PK id etc. are included)).
However, some big sites say like amazon use parameters and values in the URL a lot. They do this since then the server side does not get over-loaded and have to keep track or hold those values. However, unless you building the next facebook, or huge + high volume web site, then session() is quite much the standard approch to keep values active for your code.
However, lets assume we toss up a grid, and the user selects that product?
We set in session() say that PK id, and hten jump to the next page say to buy that house?
Well, now if you open a new tab - even a different browser, launch that grid, select a house and jump to the page to display that information? You can't use session since as noted it will overwrite the values in the other page.
So, you can try and build a bunch of fancy classes and all kinds of handstands, but I just simply transfer the session() values to ViewState.
ViewState is per page, and session() is global to that one user.
So, if I need 10 variables and value for a given part of the application, say like this:
<Serializable>
Public Class clsProjectInfo
Public ContactID As Integer = 0
Public ContactGeneralID As Integer = 0
Public PortalComp As String = ""
Public ProjectHeaderID As Integer = 0
Public QuoteNum As Integer = 0
Public UploadGroup As Integer = 0
Public txtNotes As String = ""
Public ProofID As Integer = 0
End Class
So say in the page that we select the project - setup a whole buch of values?
Then the above var will be in session(), but ONLY for pasing to the next page.
So the project view page that needs all of the aobve values to work?
The page load code will look like this:
Dim cPinfo As New clsProjectInfo
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
cPinfo = Session("Pinfo")
ViewState("Pinfo") = cPinfo
Else
cPinfo = ViewState("Pinfo")
End If
So we only EVER use session to pass that "bunch of values" to the next page, but always first thing we do is transfer from session() to viewstate. That way if the user opens another tab, or selects a different house in that 2nd browser, jumps to the view details, we only ever used session() to pass the values to the next page, but from that point on, always used ViewState.
Now the above simple idea might not work for all cases but it does for most.
So, don't adopt huge number of session() values, and as always even in desktop, global vars and values should not be required.
So session() for a given user will most certainly often "stomp" on top of other parts of the application. If session() is spilling over between different users? That should not occur, never occur and means as noted something else is going wrong here.
So even for desktop software? Each form, or page or part of the application tend to have and need a set of values. So, I build plane jane simple class as per above. And then you can with great ease pass ONE class thing with the 5-10 variables in it. that way I don't wind up with 50 variables in session() - which is a nightmare from coding point of view (let alone to remember the varaiables). But with above passing the one class, then you just passing ONE thing and you get intel-sense too boot.
And no doubt that group of variables often has to be passed to routine. So beofre above, I often like had to pass like 5-6 values to some function or sub - and what a pan.
So old way:
Call SaveAfterUpload(AjaxFileUpload1, session("QuoteNum", session("ContactID",
session("UploadGroup"), strCleanFile, session("txtNotes"), session("PortalComp")
but now we can go:
Call SaveAfterUpload(AjaxFileUpload1, cPinfo.QuoteNum, cPinfo.ContactID,
cPinfo.UploadGroup, strCleanFile, cPinfo.txtNotes, cPinfo.PortalComp)
But, then again, since we have that class, then above now becomes
Call SaveAfterUpload(AjaxFileUpload1, cPinfo)
So, don't put a truck load of values into session().
Create "groupings" of the values.
And what is even SUPER great?
often some of the client side JavaScript code needs those values.
So, you THEN wind up dropping in a boatload of hidden fields or hidden cnotrols for those values.
But, with the above class? I can pass + have the whole mess client side like this:
cPinfo = ViewState("Pinfo") ' this no doubt occured on page load
' copy Pinfo to browser side
MyUpLoadInfo.Value = JsonConvert.SerializeObject(cPinfo)
MyUpLoadInfo is just a simple asp.net hidden field like this:
<asp:HiddenField ID="MyUpLoadInfo" runat="server" ClientIDMode="Static" />
But, now in place of those 5-6 hidden fields, I have the above cPinfo now for use in the client side.
eg this:
MyUpLoadInfo = document.getElementById("MyUpLoadInfo")
Pinfo = JSON.parse(MyUpLoadInfo.value)
// now I have all values such as
Pinfo.txtNotes
Pinfo.QuoteNum
So, by building that class or set of variables, I can now pass down the WHOLE mess in one shot to the client side, and now my JavaScript code can with great ease use all those variables client side!!
And it turns out that for each section of a typical application?
about 5-10 variables are only required
often they are required client side - and with above we can
Most if not ALL of the routines in that application part need those vars
(including subs and functions, so now we can pass 5-10 values, and we don't
have huge long list of messy parameters in all of those subs and functions).
We can modify the class - add more variables, and the dozen routines now all have that extra variable - yet we don't change the code, or even change the sub/function calls to have that extra new variable in that code.
(and this applies to client side js code if we need/require that group of values for the browser code.).
So we thus don't have huge numbers of global vars.
we don't wind up with a gazillion number of separate session values.
we vast improve the ability to pass those values to subs/functions.
And we even can pass that set of values to the client side with great ease.
So any global var can be in session, but those global vars are NEVER to be used for passing value from page to page and code calls for a given part of your application.
And if you want to support more then one page or browser by the user? Then adopt the standard that on first page load you transfer that session class to viewstate.

How to save value across postbacks for a composite control without using viewstate

I have a composite control that has a couple of private fields that reference values in the cache and these private fields are called during the constructor method. Since a string key is used to identify the value in the cache, I must have a way of storing that string key in such a way that it is available at the time the control is instantiated, and I have to be able to reference it on postbacks without it changing.
In addition, this key is generated the first time the control is loaded, but it should not be changed again after that first time.
How can I accomplish this?
I have already tried saving it to viewstate, but that doesn't work because viewstate is not yet available at the time the control is instantiated.
I have tried using a private field and then checking against Page.IsPostback in the constructor and if it isn't postback, I assign a value to the private field, but on subsequent postbacks it looses it's value, and I can't reassign it in the Page.IsPostBack again because it is an autogenerated GUID.
This has got to be something folks have had to do before....
There isn't a lot of state info available during control construction at all, so this could be difficult. Is there some reason you can't move your code which accesses the Cache'ed info into the control's Init event?
I assume you can't use Session because the information stored is related to that specific request/postback. If it's not specific to that request, using Session could be a possibility - but I think you may encounter other problems trying to deal with control state so early in the lifetime.
After seeing your comment to the other answer; you should be able to move your code that checks for the cached datasource into the control's Init or even Load event, so the state will be available.
Also, incidentally; are you sure you really need to cache this data? That could end up taking up a lot of server memory.
Have you tried Session?
You can store anything you like in the session object for one particular user, maintaining the value / object between postbacks.
If you want to store on a global basis and not per ser basis, try Application
Although this isn't the best solution (rearranging your logic to fit the lifecycle model generally is), have you tried accessing the Request directly? I once really wanted to get the selected value off a DropDownList very early in the lifecycle so I could adjust some elements in the building, and I did it like this:
myDropDownList.SelectedValue = Page.Request.Form[myDropDownList.UniqueID];
So instead of waiting for the viewstate to load the server-side proxie's values, I just got it myself from the client-side control value that was passed in on the post. I probably would do things differently if I redesigned that page, but it seems to have worked out alright for now and it solved the problem I was having.

What options to I have to pass a value between two asp.net pages

I want to know what the best practice is for passing values (sometimes multiple values) between two asp.net pages
In the past I have used query strings to pass a value in asp like this:
href='<%# Eval("TestID","../Net/TestPage.aspx?TestID={0}") %>'><%#Eval("Title")%> </a>
I assume you can do this in the code behind but I do not know the best way.
I also assume it is possible to pass more than one value.
Could someone provide me with a VB snippet which would give me an idea of how to go about this?
You have many options for passing data and all of them can pass multiple values between pages.
You can use the Request.Form collection to capture values that have been submitted from an HTML form with the POST verb (i.e. " method="POST">).
The code looks something like:
Dim formvalue As String
formValue = Request.Form("FormField1")
You can also use parameters in a URL query string (much like you example):
Dim queryStringValue As String
queryStringValue = Request.QueryString("QueryStringValue1")
You can set a cookie (it's lifetime will depend on the Expiry property value that you set):
Setting a cookie (note: you use the HttpResponse object here. The user's browser stores the cookie when it receives the Set-Cookie HTTP header value from the response to a request)
Response.Cookies("CookieValue") = "My Cookie Data"
Response.Cookies("CookieValue").Expires = DateTime.Now.AddDays(1) ' optional, expires tomorrow
Retrieving a cookie value (we use the HttpRequest object here):
Dim cookieValue As String
cookieValue = Request.Cookies("CookieValue")
You can use the HttpSessionState object (accessible via the Session property of a page). To set a session variable:
Session["SessionValue"] = "My Session Value"
To retrieve a session value:
Dim sessionValue As String
sessionValue = Session["SessionValue"]
There's another way to pass page state between pages using Page.Transfer (see How to: Pass Values Between ASP.NET Web Pages), but I'd try and get comfortable with the above before looking into that.
As far as best practices go it really depends on what data you're passing.
Don't pass sensitive data via URLs (query strings), forms or cookies. These can intercepted in various ways
Pass sensitive data using a server-side store (like session state or a database) but consider how to keep the session ID safe.
Never trust data from outside your application (data that users have entered via a form, information read from a database, etc.). Always encode this information before displaying it again in your pages. This prevents against Cross-Site Scripting (a.k.a XSS) attacks.
Don't use sequential IDs in query strings where you're passing user-specific identifiers between pages. Say you create an Orders.aspx page that lists all orders for a customer. You pass in a CustID parameter via a query string: Orders.aspx?CustID=123. It's easy for someone to change the URL to Orders.aspx?CustID=124 and view information they shouldn't. You can get around this by doing a check that the current user is allowed to see the information, you can use an identfier that can't be easily guessed (commonly a GUID) or pass the information on the server-side.
It would help you to check out the following links:
a. Cross page postbacks
b. How to pass values between ASP.NET pages (MSDN)
c. Another article by Steve C. Orr on Passing values.
You can use a session, cookies, the query string, hidden form fields in a post request.

What is the best alternative for QueryString

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.

Should I store a database ID field in ViewState?

I need to retrieve a record from a database, display it on a web page (I'm using ASP.NET) but store the ID (primary key) from that record somewhere so I can go back to the database later with that ID (perhaps to do an update).
I know there are probably a few ways to do this, such as storing the ID in ViewState or a hidden field, but what is the best method and what are the reasons I might choose this method over any others?
It depends.
Do you care if anyone sees the record id? If you do then both hidden fields and viewstate are not suitable; you need to store it in session state, or encrypt viewstate.
Do you care if someone submits the form with a bogus id? If you do then you can't use a hidden field (and you need to look at CSRF protection as a bonus)
Do you want it unchangable but don't care about it being open to viewing (with some work)? Use viewstate and set enableViewStateMac="true" on your page (or globally)
Want it hidden and protected but can't use session state? Encrypt your viewstate by setting the following web.config entries
<pages enableViewState="true" enableViewStateMac="true" />
<machineKey ... validation="3DES" />
Do you want the end user to know the ID? For example if the id value is a standard 1,1 seed from the database I could look at the number and see how many customers you have. If you encrypt the value (as the viewstate can) I would find it much harder to decypher the key (but not impossible).
The alternative is to store it in the session, this will put a (very small if its just an integer) performance hit on your application but mean that I as a user never see that primary key. It also exposes the object to other parts of your application, that you may or may not want it to be exposed to (session objects remain until cleared, a set time (like 5 mins) passes or the browser window is closed - whichever happens sooner.
View state values cause extra load on the client after every post back, because the viewstate not only saves objects for the page, but remembers objects if you use the back button. That means after every post back it viewstate gets slightly bigger and harder to use. They will only exist on he page until the browser goes to another page.
Whenever I store an ID in the page like this, I always create a property
public int CustomerID {
get { return ViewState("CustomerID"); }
set { ViewState("CustomerID") = value; }
}
or
Public Property CustomerID() As Integer
Get
Return ViewState("CustomerID")
End Get
Set(ByVal value As Integer)
ViewState("CustomerID") = value
End Set
End Property
That way if you decide to change it from Viewstate to a session variable or a hidden form field, it's just a case of changing it in the property reference, the rest of the page can access the variable using "Page.CustomerID".
ViewState is an option. It is only valid for the page that you are on. It does not carry across requests to other resources like the Session object.
Hidden fields work too, but you are leaking and little bit of information about your application to anyone smart enough to view the source of your page.
You could also store your entire record in ViewState and maybe avoid another round trip to th server.
I personally am very leery about putting anything in the session. Too many times our worker processes have cycled and we lost our session state.
As you described your problem, I would put it in a hidden field or in the viewstate of the page.
Also, when determining where to put data like this, always look at the scope of the data. Is it scoped to a single page, or to the entire session? If the answer is 'session' for us, we put it in a cookie. (Disclaimer: We write intranet apps where we know cookies are enabled.)
If its a simple id will choose to pass it in querystring, that way you do not need to do postbacks and page is more accessible for users and search engines.
Session["MyId"]=myval;
It would be a little safer and essentially offers the same mechanics as putting it in the viewstate
I tend to stick things like that in hidden fields just do a little
<asp:label runat=server id=lblThingID visible=false />

Resources