Rolling My Own ASP.NET Registration Wizard - c#

I imagine this is a pretty hard question to answer without sitting down and looking at all the source code, but I figured I'd post it anyway and see if you all had any conceptual thoughts on a process we're using in my company.
The project I'm working on currently is a self-registration page for our end users. Previously, we used 3 separate ASP.NET wizard controls for each of our different registration processes. It was consistantly stated by our customers that the process was very confusing, and they never knew which of the three different processes they should be completing. So after being asked to compile the three apps into one, I quickly found that the registration wizard controls are not very extensible. The new process needed to be very dynamic, so the static, one path, wizards were not fulfilling the needs.
So in an attempt to make a more dynamic process, I rolled my own registration app that relies on session state to walk a user through registration. The way I have it set up currently is using binary flag values for possible steps in the registration. I then have a integer value in SessionState that I update after a user completes a step.
Example:
const int accountNumberStep = 1;
const int userPassStep = 2;
const int usernameReclaimStep = 4;
/* code snipped */
// Code at the end of step Username/Password selection
// at the end of Username/Password selection step, update session state to record that step was finished
SessionState("stepsCompleted") = (int)SessionState("stepsCompleted") | userPassStep;
/* code snipped */
// Code at the beginning of each step to verify previous step was completed
if (!((int)SessionState("stepsCompleted") & userPassStep == userPassStep))
{
RedirectToPreviousPage(previousStep:=userPassStep);
}
Another major piece of this (this is where the dynamic piece comes into play) is using a session variable to keep track of the type of registration they are going through. In other words, they might have three different types of registration they can go through:
Normal Registration
Account Update Registration
Obtain Disabled User Account Registration
The registration type the user is completing is determined as they progress through the registration. For example, after a user types in there account number, I determine that they are not a new user, so they will be either an update, or a re-enable. This path is stored in the registration type variable in session state.
The final major piece of the app tracks text box values, radio box selection, etc. This is all stored in the SessionState as well, so if at any point in the process, the user wishes to back up a step or two, they will not lose the previous values they entered. These values are also used in the final step of the registration process in which the customer is actually registered on the back end database.
As you can see, all of this is vary similar to what an ASP.NET wizard control does. With the exception of the registration type variable. This allows me to branch the registration into different paths at any given point in the process.
Anyway, sorry again if this is confusing. I'm mainly hoping someone will come by that has done something similar and might be able to give me some advice.
Thanks in advance for your help.
CJAM

The wizard control is based on the MultiView control, which basically provides a means of showing one (and only one) view at a time. The only difference is that you are required to control navigation manually with the MultiView, therefore allowing you non-linear progression through the steps.
With the MultiView, the values of all controls in all the views will be stored in the ViewState, so there is no need to store them in the Session. Buttons within each view can post back, and based on the values of other controls, you can show a specific view.
Check out the ASP.NET Quickstart on MultiView and View controls

Related

MVC how to store user searches so user can go back

Is such a thing possible? I have a search page and the user clicks a link to edit a document and it takes them to another page.
The user then hits the browser's back button i want to take them back to the results that they left off on so they don't have to search again.
EDIT: The search works as the user enters in parameters and it goes out to the database and returns results in a type of search result viewmodel. That viewmodel is parsed out to a table format and shown on the screen. Each row has certain bits of information tied to it like the primary key and other things that the user can see.
Once the user hits edit it uses the primary key to go back to the database to get the remaining data for that row and shows them a form on screen. If the user hits the back button on the browser I want to take them back to the result set that they just viewed without having to redo the search.
The way we have it setup is when the user hits back it goes to the index method that just does a new search page.
You can do this using the HTML5 History API. For instance, when the search returns its result you can use:
history.replaceState(..);
to modify the current browser history entry. It isn't compatible with some older browsers though, so if you have to support those, this may not work properly - or you'll have to find a workaround.
There exists a couple of 3rd party angular modules out there that wraps the history API, but it should work just fine on it's own.
A way I've done this is by using a controller extension method to get the page's search state from session. I'm sure there is a better way to do this in Angular though.

Best way of sharing user specific variables around my MVC4 app

I am building an MVC4 app using razor, I have done many in the past but I want to roll some best practice in to this one in terms of dealing with variables.
The situation is that I have a logged in user (logged in meaning a windows authentication, with that user name matched to a user in my user table). That user comes with a set of profile options such as "canViewReports", "canEditPerson" etc etc.
Now, there are two prongs to those profile options. First is that my presentation layer needs to customise itself depending on what is presented to it. So profile 1 has canViewReport set to false so the tab for reports will be hidden. Profile 2 has it true so the tab will be shown.
The second prong is that if my savvy users type in /reports/index, I need to pick that up and block access for profile 1 but allow profile 2.
Ok, so at the moment I am using a base controller that is decorated with a [UserDataFilter], that user data filter calls a method that checks the current session for a set of keys and if they are missing assigns them. I put each of those profile options for the current user in to the session. I can then use the session variables in my presentation layer and also in code.
My concern is that that is messy looking in my code having to put this kind of thing everywhere:
(bool)session["canViewReports"] everywhere in my razor.
That lead me to try using the viewstart.cshtml and setting App variables in there that I can use in my razor a bit cleaner. In viewstart I set:
App.canViewReports = (bool)HttpContext.Current.Session["canViewReports"];
I can then just use App.canViewreports everyhwere in my views without too much trouble.
My questions are:
1) Is App.canViewReports for the entire application at an IIS level, or does each connection to IIS get its own pool of App. variables. The thing I want to avoid is the first user setting the variable and every other user that subsequently uses the application getting that value! (on different computers)
2) Is there a better way of doing this!!!
Many thanks
Iain
I would use User.IsInRole("canViewReports") in my razor logic to hide and show the menus item. If you build you menu in you layout you only need to do this once.
I would then further protect the action method by decorating the method with
[AuthorizeUser("canViewReports")]
You could create an ISessionService that stores the session information you need.In this way,you can implement it however you want and have full control over it.It could be retrieved easily via a DI container and it's easy to mock.

Best practice for persisting User State in special kind of Asp.Net Mvc application

I will try to explain my situation and what I wanted to do. There is not any difficult and rare situation, but I can't find any relative questions or articles in internet.
I have created a web application on ASP.NET MVC 5. Users are not going to enter my application directly. Users will enter let's say to CentralInformationSystem.com. Then they must login to this website one of supported ways. After signing in, they will see a list of applications. There will be applications which has been allowed to use for the signed user. One of this applications will be my application which has developed in Asp.Net MVC.
And the main point is that our applications will not be opened in other tabs or in current tab and so on. Our application will be opened in a big iframe inside the current tab.
And other main point is our applications and CentralInformationSystem.com belong to other domains.
The other question of course is, how then I can now which user has signed in? And the answer is, CentralInformationSystem.com sends encrypted data with the query string to our web site. For example, the URL will look like that:
MyMvcApplication/Home/Index?Token=jkndid758adsai==qwdbqwiudhqwadoqidwqq=wqdiqw
Also keep in mind that they will always sent different tokens.
And after that, I will decrypt token and find to which user it belongs. Also keep in mind that, one Token can be used only once.
1. What type of application is my application?
User will enter very big form. It can actually take almost 3-4 hours. So, I have tried some-type of wizard logic. After entering some portion of datas, I will insert them to the database, get identifier from the database and store it somewhere and take the user to the next level and so on.
2. What I want to achieve?
I want to create such logic that, some identifier variables values must be stored in such place that never must be expired till the user closes browser or signing out. I don't want to increase session timeout to 5-6 hours.
3. What if user opens my application in more than one tab?
Alongside 2 I have also one problem, that user can open my website inside iframe more than one tab. I know that, in Asp.net we can differ session per each tab. But, I don't want to store datas in session, because user can stop filling forms after 20 minutes or 4 hours. Also, I cannot use cookie, because cookies will be same for all tabs.
My other option is, to inject hidden inputs with encrypted value to all views. But, I can't find how to automatically add these datas to each views. Also, it doesn't seem to me as most efficient way.
The other logic is to prevent user to open same application in more than one tab with differen tokens. But, don't how to achieve this also.
Additional:
I have read almost all articles and questions/answers. I know how to make it work. But, I want the best approach. Neither of my approaches are efficient.
Use your own concept of a persistent session that is identified by a hidden input on the page and does not expire, or at least does not expire for a very long time. Have all of your controllers derive from a single base controller and use the OnActionExecuted to add the session "key" to the ViewBag when the result is a ViewResult (you won't need it for partial views or JSON, etc). Every page can then access the ViewBag and create the hidden input - probably you want to use a partial view for this and simply include the partial on every page. Store the data associated with this session in the database.

What is the proper way to handle several automatic redirects?

I have a website that basically allows customers to build a cart with an item that can be configured. A user will pick an item, and they'll be prompted to pick the first option they want, they get sent to the second step where they pick their second option, etc.
The number of steps and the number of options are variable, as they are defined by the client. Usually the item only has 2-3 steps with 5-10 options. However, in order to make it faster for the customer, if there is only one option available for the given step, it will automatically be chosen and the user will be sent to the next step.
A client decided to set up an item with 10+ steps with only one option for each step. This results in the entire process automatically choosing everything. FireFox doesn't like this, as it gives the error
Firefox has detected that the server
is redirecting the request for this
address in a way that will never
complete.
(I haven't checked IE or Chrome, although it probably gives similar errors).
What's the best way to fix this?
Right now the process is basically
User picks item
User picks option if there is more than one option available. Otherwise the website does step 3 itself.
POST to add the option to the cart
Redirect to Page.aspx?step=#
Repeat step 2-4 as many times as necessary
Is there any change I can make to the code or page so that FireFox doesn't think I'm in an endless loop?
I am surprised that you get an endless redirect error if # is different each time, but either way, this doesn't seem like the best architecture. Basically, if the code decides a step can be done automatically, it instantly redirects to the same page with the new step number?
Why don't you just have your code do that without redirecting, increment the page number in the server code as needed, and show them the right step directly, without having to redirect?
Whatever is happening when you POST at each step I would think you can accomplish just as easily in code without actually having to do a new post.
I'm guessing something like this would work:
Read Step # from query string into local variable
Load data from database passing in the local step variable
If data only contains one option then:
(3.1) Store option
(3.2) increment local step variable
(3.3) goto 2
Load page with data from step 2
How does your code handling someone skipping options and entering Page.aspx?step=10 into the address bar when they are on Page.aspx?step=1?

How do I force users to not skip pages via url input in an asp.net mvc 2 application?

I'm using ASP.NET MVC 2 & VS 2008 Pro.
I am building a web site where users must go from page A to page B to page C. In other words, the order is very important. I want to guard against a user on page A simply entering a url for page C. If the user attempted to do such a thing I'd like to redirect them back to the page they were on before they entered the url.
It seems like I would make an action filter for this, but I'm honestly not sure what kind I would need. I would need access to the url the user was on and the url they are going to, and would have to validate them.
Thoughts?
EDIT 1
This is ugly, but it looks like I can get the full Uri's for the referring & destination urls inside of the OnActionExecuting method. I'd wager that this could be done from any kind of action filter. I'm currently testing all of this inside of the OnActionExecuting event of a custom action filter initially designed to check session state for expiration.
LogUtil.Write("OnActionExecuting", String.Format("Referring Url: {0} \n Destination Url: {1} ",
filterContext.RequestContext.HttpContext.Request.UrlReferrer.AbsoluteUri.ToString(),
filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.ToString() ));
LogUtil is just a custom class I wrote that writes out to a log file.
So far it isn't pretty, but it works. Anyone have a more elegant solution?
EDIT 2
Another take that makes comparing the url's a bit easier is below. I haven't tried this using routes that actually contain parameters. In that situation, this might get thrown off.
String[] referrerSegments = filterContext.RequestContext.HttpContext.Request.UrlReferrer.Segments;
String[] destinationSegments = filterContext.RequestContext.HttpContext.Request.Url.Segments;
Perform action lookup logic to ensure destinationSegments[destinationSegments.length-1] comes after referrerSegments[referrerSegments-1]. This will likely be done with a static string List that contains the names of all the actions in the application in order. The index values of these shouldn't be more than 1 apart (i.e. destination action should have an index of plus or minus 1 of the value of referring action index).
Thoughts?
EDIT 3
Sigh. Apparently the referrer information is lost when a user is on a page and manually types in the url into the address bar. This seems odd to me, but it means I can only get the url for the current page the person is on.
Anyone have any suggestions here aside from session? I really, really want to avoid storing something like this in session if possible.
An Action filter is exactly what you need, with a redirect when they are being naughty. Keep track of their progress in the time honoured fashion, using the Session State.
This keeps the main part of your application relatively stateless (compared to classic ASP.NET) and keeps this logic in the action filter.
Another consideration: If your input is potentially a long-lived process you could save the user's progress to a database or into a Windows Workflow object. Just a thought. This would enable them to start the process on one machine and pick it up elsewhere. This might not apply to your scenario, but it is worth considering in some situations.

Categories

Resources