I'm developing a simple test website using ASP.NET MVC3 and Razor syntax. It has _LayoutPage.cshtml as master template which uses #HTML.Action to print user id at the top of the site for each page.
I implemented a childAction named userInfo for this partial view which reads the user id from HTTPContext.Session and prints it out. The child action is implemented in a controller called CommonActionController derived from Controller. In addition to user id it also reads two more variables from session and prints it.
public class CommonActionController: Controller
{
[ChildActionOnly]
public ActionResult userInfo()
{
if(HTTPContext.Session["x-user-id"] != null)
{
ViewBag.UserId = (string)(HTTPContext.Session["x-user-id"]);
ViewBag.UserFirstName = (string)(HTTPContext.Session["x-user-first-name"]);
ViewBag.UserLastName = (string)(HTTPContext.Session["x-user-last-name"]);
ViewBag.UserLoggedinSince = (DateTime)(HTTPContext.Session["x-user-logon-timestamp"]).ToString("f");
}
return PartialView();
}
}
My main page controller called HomeController has the dashboard functionality implemented in Dashboard action (currently it just prints the word "Dashboard"). In this controller I have overridden Controller.OnActionExecuting() method which validates that the user id exists in session. It reads total three variables from session just like the aforementioned childAction.
public class HomeController: Controller
{
public HomeController()
{
}
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
if(HTTPContext.Session["x-user-id"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
if(HTTPContext.Session["x-user-logon-timestamp"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
if(HTTPContext.Session["x-user-internal-flag"] == null)
ctx.Result = new RedirectResult("logon/userlogon");
}
public ActionResult Dashboard()
{
// nothing to see here
return View();
}
}
I have cleaned up the code little bit to remove the debug.print statements.
As per the logs I see that the OnActionExecuting() method and userInfo child action are invoked simultaneously. At one point OnActionExecuting() gets nulls for session variables. In the log I can see that until the point ChildAction is invoked, session variables hold their value within OnActionExecuting(). Once the childaction accesses them, they become null.
When I comment the code that accesses session from child action, everything works fine. What am I doing wrong? Is there some precaution I have to take while accessing session variables? Is this due to my ignorance about how to access Session asynchronously?
I also have following in my web.config:
<modules runAllManagedModulesForAllRequests="true"/>
1) Start–> Administrative Tools –> Services
2) right click over the ASP.NET State Service and click “start”
*additionally you could set the service to automatic so that it will work after a reboot.
For more details you can check my blog post: http://jamshidhashimi.com/2011/03/16/unable-to-make-the-session-state-request-to-the-session-state-server/
ref:
Unable to make the session state request to the session state server
Related
Example:
We have two user types.
SupplierUser
FactoryUser
They both interface with basically the same data, but in general FactoryUsers can edit much more of this information than the SupplierUser.
Using ASP.NET 4.5, I am implementing all of this using MVC.
Some summarized use cases: (Assume logged in)
FactoryUser:
Editable messages page, which shows suppliers their latest announcements.
Order confirmation page, and order view page.
Supplier edit page (for updating addresses etc of multiple suppliers)
SupplierUser:
- Can see messages from specific Factory.
- Can create orders, send and view.
- Can edit their own information
As you can see this is just a lot of editing of information with various permission. My question is, Where should I be starting the separations?
With regards to:
Models - I think this one stays as one with the database
ViewModels - Do I write different views for each role? If so, 2 files/classes?
Controllers - Same, do I write different functions?? Classes? If so then what is the point is having [Authorize role], just to protect from unauthorized access & not intended to split?
Views - Do I try to use the same views for most parts and just somehow include logic about if they have "edit" buttons or not?
Partial Views - Can they be used for the "edit" buttons that may or may not be on the view?
View Layouts - ?
Filters - I can do some fancy logic and put everything in entirely 2 different folders (the whole MVC) and then split it at route/authorize level
Routing - ?
At each one of the above, I can see a possibility to split the logic depending on the type of user. But of course, I want to do this in the simplest and most sane way possible.
Is there some document somewhere which specifies how this should be done, or otherwise any wise people out there who have done this before and encountered all the issues?
Thanks
(first question!)
One way to do this is to create features. e.g View Orders, Create Order, Update Order, Delete Order
These features will then be assigned to a Role - and the Role can be assigned to a User
So the DB will look something like this:
Now when the user logs in, you read all the features assigned to the user and save them in the session (Create a SessionHandler Class).
// Login Function - You can call from Controller
public UserDTO Login(string username, string password)
{
var user = dbContext.Users.FirstOrDefault(s => s.Username == username && s.Password == password);
if(user == null) return null; // login failed
var model = new UserDTO()
{
UserId = user.UserId,
Features = user.Role.Features.Select(s => s.FeatureName).ToList()
};
return model;
}
Sample of UserDTO class
public class UserDTO
{
public int UserId {get;set;}
public List<string> Features {get;set;}
}
Sample of SessionHandler
public class SessionHandler
{
private const string SessionKey = "UserSession";
public static UserDTO UserSession
{
get
{
return HttpContext.Current.Session[SessionKey] != null
? (UserDTO)HttpContext.Current.Session[SessionKey]
: null;
}
set { HttpContext.Current.Session[SessionKey] = value; }
}
}
So in your controller call the Login Function and assign to UserSession in SessionHandler
[HttpPost]
public ActionResult Login(LoginModel model)
{
var user = Login(model.username, model.password);
if(user == null) return View(model);
SessionHandler.UserSession = user;
// TODO: redirect to Home Page - after login
return RedirectToAction("Index", "Home");
}
Then what you can do in your views is check if the user can perform a certain action, so e.g. if you are on the View Orders page - you can hide the Create Order Button if user does NOT have permission:
#model WhateverDTO
// Check if user has Create Order Feature in Role
#if (SessionHandler.UserSession.Features.Contains("Create Order"))
{
// Yes, User has permission - then Render the Button
<button> Create Order </button>
}
Also you can add checks in the Controller(Server side) - Which will provide extra security to your application, using the Authorise Attribute:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (httpContext.Session == null)
return false;
// Checking Authenticaiton
var userSettings = SessionHandler.UserSession;
if (userSettings == null)
return true;
//Checking Authorization
if (Roles.Length == 0)
return true;
var actionFeatures = Roles.Split(',');
if (!actionFeatures.Any(s => userSettings.Features.Contains(s)))
throw new UnauthorizedAccessException("You do not have permission to perform this action.");
return true;
}
}
and then decorate your Actions,
[CustomAuthorize(Roles = "Create Order")]
// Pass feature name for Roles - if user doesn't have permission to Create Order - the "You do not have permission to perform this action." exception will get thrown
public ActionResult CreateOrder()
{
return View(new CreateOrderDTO());
}
[HttpPost]
[CustomAuthorize(Roles = "Create Order")]
// Pass feature name for Roles - if user doesn't have permission to Create Order - the "You do not have permission to perform this action." exception will get thrown
public ActionResult CreateOrder(CreateOrderDTO model)
{
return View(model);
}
The good thing about the above method - is that you can add as many user Roles as you need - without changing the Code.
Models - I think this one stays as one with the database
Models are same - same DB same models
ViewModels - Do I write different views for each role? If so, 2 files/classes?
No, don't complicate things - use same ViewModel / DTO
Controllers - Same, do I write different functions?? Classes? If so then what is the point is having [Authorize role], just to protect from unauthorized access & not intended to split?
No need for separate actions/views or controllers
Views - Do I try to use the same views for most parts and just somehow include logic about if they have "edit" buttons or not?
Yes, use same views - Hide/Show actions based on User Role/ Feature
Partial Views - Can they be used for the "edit" buttons that may or may not be on the view?
No need for Partial Views for buttons
View Layouts - ?
Filters - I can do some fancy logic and put everything in entirely 2 different folders (the whole MVC) and then split it at route/authorize level
Routing - ?
No
This is my controller code:
private string testVal;
public ActionResult Index()
{
testVal = "test";
return View();
}
public ActionResult NextView()
{
if (testVal == null)
Debug.WriteLine("testVal is null");
return View();
}
Is it possible to remeber values like testVal after changing page? It seems that when redirecting it resets values (testVal in NextVal is null).
Edit:
I try to save values to session but Session is null. I am using SignalR and when user is connected to page i use static event from hub to inform controller that user has connected - but inside method that runs on that event Session is unfortunetly null.
My controller code:
public ActionResult Index()
{
LoadingHub.userConnected += new EventHandler<IdEventArgs>(UserConnected);
return View();
}
private void UserConnected(object sender, IdEventArgs e)
{
Debug.WriteLine("User Connected with Id: " + e.Id);
if (Session == null)
Debug.WriteLine("Session is null");
}
My signalr hub:
public class LoadingHub : Hub
{
public static event EventHandler<IdEventArgs> userConnected;
//Function informs server that user has connected
public void Connected()
{
Debug.WriteLine("Hub Connected Method");
var id = Context.ConnectionId;
userConnected(this, new IdEventArgs(id));
}
}
Every time that you make a request a new instance of the controller is created so using a private field you will not be able to retain the value of this variable.
The easiest way for you to retain it it is to use a session. (if you want to retain this value per user base)
for example in your code
public ActionResult Index()
{
System.Web.HttpContext.Current.Session["testVal"] = "test";
return View();
}
public ActionResult NextView()
{
if (System.Web.HttpContext.Current.Session["testVal"] == null)
Debug.WriteLine("testVal is null");
return View();
}
you can use cookie or cache to replace the variable.
when you redirect to a webpage ,the controller will be newed ,so you cannot get the right testVal .but the cookie is stored in broswer .so you can set it and get .
You may use session or Pass the data to the controller
Have you looked into ASP.NET server side state management click here.
These are basically different ways to remember a value on the server once a new page has been loaded.
So a few server side techniques you could use to remember testVal are Session State or Application State. However Session State is more suitable for your scenario as it is only specific to the user's current session whereas Application State stores data that can be shared between sessions and would therefore be more ideal for global variables.
You can read the link I provided to read more on the differences though.
I would also like to warn you (as some say to use cookies), the user can delete or disable or manipulate them on the browser so this isn't an ideal solution.
I have created my own customization for the AuthorizeAttribute inside my asp.net mvc web application, and to be able to return the user to the current URL after login , i am trying to save the current URL inside a TempData and then redirect to the login action method ,as follow-
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!_authorize && !filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = new RedirectResult("/Account/Login");
TempData["returnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.Result = viewResult;
}
but seesm that i can not reference TempData in this case, because the above code will raise the following error:-
The name 'TempData' does not exist in the current context
Can anyone advice please?
Thanks
Try using controller base,
filterContext.Controller.TempData["returnUrl"] = filterContext.HttpContext.Request.Url.PathAndQuery;
Also TempData may behave unexpectedly in Authorize attribute as it lives only on one request cycle. If so use Session instead.
I have an ASP.NET MVC4 front end as one project in my solution, and a separate ASP.NET Web API as another project in the same solution. The Web API will contain all of my CRUD operations.
2 questions
How do I call my Web API from my front end to perform CRUD operations? I have my entity data model defined in my Web API project, and I will need to bind my front end views to it, how would I do that?
Once this is deployed to my web servers, the front end will reside on one server, and the Web API will reside on another server (the server that holds most of our web services). So, I guess along the same lines, how would I call the Web API from my front end once deployed? I understand Web API's are simply called with an HTTP request, but in terms of passing my models (which are defined in my Web API project) into my Views (in my front end project), how can I do this?
While Kevin is right, I did this the non-Ajax way. Keep in mind that I am working with JSON data, so this is centered around JSON.
In your controller page, remove anything that has to do with DbContext, Entity Framework, etc. The reason is by default, the controller will want to perform CRUD operations by calling the DbContext, and we don't want this. We want to call the WebAPI instead to do this.
First and foremost, declare some member variables in your controller. The rest of your controller will utilize these:
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
Uri contactUri = null;
In your controller, create a constructor for your controller, as such:
public ContactController()
{
// set base address of WebAPI depending on your current environment
client.BaseAddress = new Uri("http://server/YourAPI/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
Replace the Index action's code with something like the following. Note that the only relevant pieces are the client.GetAsync() call and the var contacts assignment. Everything else is not necessary for the context of this problem. The value inside the client.GetAsync() should be the name of your controller, prepended by any custom routing you set up in your WebApiConfig.cs - in my case, I added the api part in my route to distinguish between API calls and normal calls:
public ActionResult Index()
{
response = client.GetAsync("api/contact").Result;
if (response.IsSuccessStatusCode)
{
var contacts = response.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
return View(contacts);
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Create action (the HttpPost action) with something like the following. Again, the only important piece is the client.PostAsJsonAsync() part - this is what calls the WebAPI's POST action which takes care of, in my case, inserting a new record into the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Contact contact)
{
// Create a new product
response = client.PostAsJsonAsync("api/contact", contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Edit action (the non-HttpPost action) with something like the following. This was a little tricky because in order to edit, you had to retrieve the record first, so basically, the HttpPost version of Edit will contain somewhat similar code, with an additional line of code that performs the edit POST (PUT). Below, we're getting the response from the WebAPI by passing it a specific record ID. So, just like for Index (GET), we are doing the same thing only passing in the ID so we only get back one record. Then, we cast the response to an actual object that can be operated on in the View:
public ActionResult Edit(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Replace the Edit action (the HttpPost action) with something like the following. Below, we're getting the record to be edited by calling client.GetAsync() and passing in the primary key as a parameter (contact_id). Then, we're getting the RequestUri from that response and saving it. Then, we're calling client.PutAsJsonAsync() and passing in the Uri.PathAndQuery (what we just saved) as well as the object to be edited.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Contact contact)
{
response = client.GetAsync(string.Format("api/contact/{0}", contact.contact_id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.PutAsJsonAsync(contactUri.PathAndQuery, contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Delete action (the non-HttpPost action) with something like the following. So again, we're getting the record from the database by simply calling client.GetAsync() and casting it to an actual object my app knows of.
public ActionResult Delete(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Finally, replace the Delete action (the HttpPost action) with something like the following. Again, we're doing something similar to that of the Edit action. We are getting the record to be deleted, casting it to an object, and then passing that object into a client.DeleteAsync() call, as shown below.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.DeleteAsync(contactUri).Result;
return RedirectToAction("Index");
}
You can call your Web API from the client using jQuery ajax method. But since you are calling from a site other than where the Web API is deployed you will have to use JSONP, instead of JSON. Take a look at this QA to see how you use JSONP with Web API. Your models will be passed as JSON which you will have to render on the client side, instead of using Razor to render it on the server side. I would use something like Knockout to create a View Model on the client that will bind your model to the HTML elements on the client.
I have a very strange problem: NHibernate crashes when being called from a web user control.
I am working on a ASP.Net (2.0) web page which uses NHibernate to access a database.
And I have a simple factory class to access a column CurrentStepNumber in the table ProjectInfo:
public class ProjectEntity
{
private int? _currentStepNumber;
public virtual int? CurrentStepNumber
{
get { return _currentStepNumber; }
set { _currentStepNumber = value; }
}
public static ProjectWizardEntity GetById(int id, bool shouldLock)
{
return RepositoryFactory.ProjectWizardRepository.GetById(id, shouldLock);
}
public static ProjectWizardEntity GetById(int id)
{
return GetById(id, false);
}
public virtual void Save()
{
RepositoryFactory.ProjectWizardRepository.Save(this);
}
public virtual void SaveOrUpdate()
{
RepositoryFactory.ProjectWizardRepository.SaveOrUpdate(this);
}
}
This class is accessed via a proxy class, so that everytime a new value is assigned it is flushed to the database:
public class DBHelper
{
ProjectEntity _projectEntity;
ProjectEntity GetProjectEntity()
{
if (_projectEntity == null)
_projectEntity = //get or create a new one;
return _projectEntity ;
}
public int? CurrentStepNumber
{
get
{
return (CurrentProjectId > 0) ? CurrentProjectWizardEntity.CurrentStepNumber : 0;
}
set
{
if (CurrentProjectId > 0)
{
CurrentProjectWizardEntity.CurrentStepNumber = value;
CurrentProjectWizardEntity.SaveOrUpdate();
}
}
}
}
Now the problem:
When I access CurrentStepNumber from the test.aspx page, everything works perfectly
When I read this field from the web user control (test.ascx) which is used on test.aspx page it is still OK
However when I try to assign a value to CurrentStepNumber in the code behind the control (test.ascx) I always get an exception:
NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Actually SaveOrUpdate method of the NHibernate Repository throws the exception.
I could not figure out what could be the problem here, so any help will be appreciated.
I think that You have a session management problem here. How is RepositoryFactory.ProjectWizardRepository.GetById creating and possibly disposing the NHibernate session? Does it create and then close a session?
It seems that DBHelper.GetProjectEntity() is creating or loading a ProjectEntity. Later, when CurrentStepNumber's setter i called, You insert or update the object to the database.
The problem is that when DBHelper.GetProjectEntity() is loading an existing object and later closing the session after it has been loaded but keep the object around, we are entering deep water. When the object is later updated, You set a new value for CurrentStepNumber and send the object to NHibernate to save. The problem here is that the object is not associated with the new session that is created and closed during saving. Nhibernate then gets confused since a new object is discovered that is not loaded from current session but it has an id of an existing object.
For a solution, Google for "NHibernate asp.net session management" and You will get a number of good hits about how to use the ASP.NET request cycle as a unit of work.