Writing to a read only session in MVC 3+

2019-01-23 12:04发布

问题:

I've come across a curious behaviour of ASP sessions. You can force a controller to be outside of the user's session - I want to be able to do this so that multiple requests can execute at the same time and using a session causes them to execute consecutively.

Disabling session state works as expected:

[SessionState(SessionStateBehavior.Disabled)]
public class SampleController : Controller
{
    public ActionResult Test() 
    {
        // Access to the session should be denied
        object test = Session["test"];
        return Content(test);
    }
}

Going to ~/Sample/Test will throw a System.Web.HttpException, as expected. However, read-only sessions appear to behave a little strangely:

[SessionState(SessionStateBehavior.ReadOnly)]
public class SampleController : Controller
{
    public ActionResult Test() 
    {
        // Read from the session should be fine
        object test = Session["test"];
        return Content(test);
    }

    public ActionResult SetTest(string value) 
    {
        // Write to the session should fail 
        Session["test"] = value;

        // Read it back from the session
        object test = Session["test"];
        return Content(test);
    }
}

So now I expect ~/Sample/Test to work, and it does. The odd bit is that the set does too: I go to ~/Sample/SetTest?value=foo and it doesn't throw an exception, in fact it returns "foo". If I call ~/Sample/SetTest?value=bar and then ~/Sample/Test I get "bar", indicating that the session has been written to.

So in a SessionStateBehavior.ReadOnly I have successfully written to the session and read my value back.

I think this could be due to one of three things:

  • In MVC 3 [SessionState(SessionStateBehavior.ReadOnly)] is broken/ignored.
  • The [SessionState] is overridden when the session is written to and becomes writeable.
  • The SessionStateBehavior.ReadOnly actually indicates some kind of dirty/optimistic access.

Can anyone confirm?

I suspect the last one is true, based off the custom session provider documentation - if it is then how does the implementation work? Does writing to a 'read only' session risk concurrency errors (i.e. last write wins) or does it risk corrupt sessions and breaking exceptions?

Update

It looks like this is by design (from Microsoft's docs):

Note that even if the EnableSessionState attribute is marked as ReadOnly, other ASP.NET pages in the same application might be able to write to the session store, so a request for read-only session data from the store might still end up waiting for locked data to be freed.

It looks like the second option above is what it actually does - the session is locked and the mode changed to writable.

回答1:

~/Sample/SetTest?value=foo

Yes it won't throw any error, but it also didn't persist the session at the end of the request. By design any thing that you write to session gets updated (only if the session is writable) at the very end of the request life-cycle.

In my test ~/Sample/Test returns nothing.

I think they should have failed fast here when the session is readonly.

By the way your sample need to be rewritten

string test = (string)this.Session["test"];