IIS7 上的 Spring.NET 和 MVC3 - 会话范围行为

发布于 2024-11-08 14:51:20 字数 1395 浏览 0 评论 0原文

可能这是一个愚蠢的问题,我根本不明白 Spring 和 IIS 是如何工作的,但让我们尝试一下。

我对 ASP.NET 很陌生,据我所知,会话处理类似于 Apache/PHP:

会话在浏览器的选项卡之间共享,但不在不同的浏览器之间共享。即,如果我在 Firefox 中打开页面并将某些东西放入购物车,购物车仍将在另一个选项卡中包含我的商品,但在 Internet Explorer 中打开同一页面应该会显示一个空购物车。

但是我无法重现这种行为使用 Spring.NET。

我用购物车对象创建了一个 hello-world,该对象在会话范围中注明:

<objects xmlns="http://www.springframework.net">
  <object id="shoppingCart" type="DemoShop.Models.Cart.ShoppingCart, DemoShop" singleton="true" scope="session" />
</objects>

如果我将某些东西添加到购物车中,它会在任何选项卡和浏览器中持续存在。所以在我看来,这个对象好像是一个真正的单例,因此在 IIS 应用程序运行时是持久的。

我知道,你会说:为什么我在 spring 配置中使用属性 singleton="true" ?如果我删除它或将其设置为 false,那么该对象将不会保留在会话中,而是会在每个请求时重新创建,从而丢失其数据。

Spring.NET 文档根本没有谈论 MVC 上下文中的 singleton 属性,我花了一些时间才弄清楚,使用 MVC3 似乎需要它。

我能够使用

<object id="..." type="..., ..." singleton="true" scope="application" />

使用请求范围对象

<object id="..." type="..., ..." scope="request" />

<object id="..." type="..., ..." singleton="false" scope="request" />

成功创建应用程序范围对象但是,将 singleton 属性排除在外,始终将我的对象放入请求范围中,无论我在 中实际注意到哪个范围范围 属性。

我的猜测是,该会话实际上并未在 Firefox 和 IE 之间共享,但购物车对象只是在应用程序范围内,因为我使用 spring 的方式错误。

任何人都可以给我建议或提示我做错了什么或者这是 IIS7 中的问题吗?

probably it's a stupid question and I simply did not understand how Spring and IIS work, but lets give it a try.

I'm quite new to ASP.NET and as far as I understand, the session handling is similar to Apache/PHP:

A session is shared between tabs of a browser, but not between different browsers. I.e. if I open my page in Firefox and put something in the cart, the cart will still contain my items in another tab, but opening the very same page in Internet Explorer should show me an empty cart.

However I cannot reproduce this behaviour using Spring.NET.

I made a hello-world with a shopping cart object which is noted in session scope:

<objects xmlns="http://www.springframework.net">
  <object id="shoppingCart" type="DemoShop.Models.Cart.ShoppingCart, DemoShop" singleton="true" scope="session" />
</objects>

If I add something into my cart, it persists across any tab and browser. So this looks to me as if this object is a real singleton and thus persistent during the runtime of the IIS application.

I know, what you are going to say: Why did I use the attribute singleton="true" in my spring config? Well if I remove it or set it to false, then the object will not persist in the session, but will be re-created on every request and thus lose it's data.

The Spring.NET documentation is not speaking about the singleton attribute in MVC context at all and it took me some time to figure out, that it seem to be required using MVC3.

I was able to successfully create application scope objects using

<object id="..." type="..., ..." singleton="true" scope="application" />

and request scope object using either

<object id="..." type="..., ..." scope="request" />

or

<object id="..." type="..., ..." singleton="false" scope="request" />

However leaving the singleton attribute out, always put my object in request scope no matter which scope I actually noted in the scope attribute.

My guess is, that the session is not actually shared between Firefox and IE, but the cart object is simply in application scope, because I'm using spring the wrong way.

Can anyone give me advice or hints what I'm doing wrong or is this a problem in IIS7?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

‘画卷フ 2024-11-15 14:51:20

这是一个错误。

对 spring.net 源代码进行一些过度调试后,我们发现:

  1. spring.web dll 提供了对会话范围对象的支持。
  2. spring.web.mvc dll 确实 提供了对会话范围对象的支持。不依赖于 spring.web
  3. 这意味着,不可能实例化一个可以解析会话范围对象的 MvcApplicationContext

以下解决方案显示了自定义 MvcApplicationContext完全启用会话范围内的对象MVC3 使用 spring.net。

标准应用程序上下文无法解析 Web 范围的原因是它使用的类 RootObjectDefinition 不知道 scope 属性(在配置 xml 中)。 WebApplicationContext 相反,实例化 RootWebObjectDefinition 类型,它知道范围。

WebObjectsFactory 重写了 CreateRootObjectDefinition 方法,该方法返回 RootWebObjectDefinition 的实例。这就是我们想要从应用程序上下文中返回的内容。这是通过重写方法 CreateObjectsFactory 来完成的。

接下来,我们必须重写方法 CreateXmlObjectDefinitionReader。当 spring 从配置中读取元数据时,如果我们不使用特定的读取器,它不会解析诸如 scope 之类的附加属性。因此,我们将在应用程序上下文中使用WebObjectDefinitionReader

对于会话范围对象的配置,您可以省略 singleton 属性或将其显式设置为 true。否则,如果值为 false,会话范围肯定会被禁用。

示例:

<objects xmlns="http://www.springframework.net">
    <object id="shoppingCart" type="ShoppingCart, ..." singleton="true" scope="session" />
</objects>

分步解决方案:

  1. 创建继承自 MvcApplicationContextMvcWebApplicationContext。您将需要重写上面提到的两个方法并创建默认构造函数。
  2. 创建一个继承自 MvcContextHandlerMvcWebContextHandler。这将触发我们的自定义应用程序上下文将被使用。
  3. web.config 中使用自定义上下文处理程序。
  4. 对于 IIS6 支持或 Visual Studio 内置 Web 服务器:将 WebSupportModule 添加到 system.web 部分。
  5. 对于 IIS7 支持:将 WebSupportModule 添加到 system.webserver 部分。

web.config:

<configSections>
    <sectionGroup name="spring">
        <section name="context" type="YourNamspace.MvcWebContextHandler, YourAssembly"/>    
        ....
    </sectionGroup>    
    ....
</configSections>

<!-- IIS6 -->
<system.web>
    <httpModules>
        <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
    </httpModules>
</system.web>

<!-- IIS7 -->
<system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true" >
        <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
    </modules>
</system.webServer>

自定义应用程序上下文类:

public class MvcWebApplicationContext: MvcApplicationContext {

    public MvcWebApplicationContext(string name, bool caseSensitive, params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(name, null, configurationLocations, null, caseSensitive))
    { }

    public MvcWebApplicationContext(string name, bool caseSensitive, IApplicationContext parentContext, params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(name, parentContext, configurationLocations, null, caseSensitive))
    { }

    public MvcWebApplicationContext(MvcApplicationContextArgs args)
    : base(args)
    { }

    public MvcWebApplicationContext(string name, bool caseSensitive, string[] configurationLocations, IResource[] configurationResources)
    : this(new MvcApplicationContextArgs(name, null, configurationLocations, configurationResources, caseSensitive))
    { }

    public MvcWebApplicationContext(params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(string.Empty, null, configurationLocations, null, false))
    { }

    protected override XmlObjectDefinitionReader CreateXmlObjectDefinitionReader(DefaultListableObjectFactory objectFactory)
    {
        return new WebObjectDefinitionReader(GetContextPathWithTrailingSlash(), objectFactory, new XmlUrlResolver());
    }

    protected override DefaultListableObjectFactory CreateObjectFactory()
    {
        return new WebObjectFactory(GetContextPathWithTrailingSlash(), IsCaseSensitive);
    }

    private string GetContextPathWithTrailingSlash()
    {
        string contextPath = this.Name;
        if (contextPath == DefaultRootContextName)
        {
            contextPath = "/";
        }
        else 
        {
            contextPath = contextPath + "/";
        }
        return contextPath;
    }
}

自定义上下文处理程序类:

public class MvcWebContextHandler : MvcContextHandler {

    protected override Type DefaultApplicationContextType
    {
        get { return typeof(MvcWebApplicationContext); }
    }  
}

我们将此错误添加到 Spring.NET 的问题跟踪器中:
https://jira.springsource.org/browse/SPRNET-1450

It's a bug.

After some excessive debugging of the spring.net source code we found out:

  1. Support for session scoped objects is given by the spring.web dll
  2. The spring.web.mvc dll does not depend on spring.web
  3. This means, it is impossible to instantiate an MvcApplicationContext that can resolve session scoped objects

The following solution shows a custom MvcApplicationContext that fully enables session scoped objects within MVC3 using spring.net.

The reason why a standard application context cannot resolve web scopes is that it's using the class RootObjectDefinition whis doesn't know about the scope attribute (in the config xml). The WebApplicationContext instead instantiates RootWebObjectDefinition types, which know the scope.

The WebObjectsFactory overrides the method CreateRootObjectDefinition which returns instances of RootWebObjectDefinition. This is the one, we want to return from our application context. This is done by overriding the method CreateObjectsFactory.

Next thing, we have to override is the method CreateXmlObjectDefinitionReader. When spring is reading the metadata from the config, it will not parse additional attributes like scope if we don't take a specific reader. Therefore we will use the WebObjectDefinitionReader in our application context.

For the configuration of your session scoped objects, you can either leave out the singleton attribute or set it explicitly to true. Otherwise with value false the session scope will be disabled for sure.

EXAMPLE:

<objects xmlns="http://www.springframework.net">
    <object id="shoppingCart" type="ShoppingCart, ..." singleton="true" scope="session" />
</objects>

Step-by-Step solution:

  1. Create MvcWebApplicationContext inheriting from MvcApplicationContext. You will need to override the two methods mentioned above and create default constructors.
  2. Create a MvcWebContextHandler inheriting from MvcContextHandler. This will trigger that our custom application context will be used.
  3. Use custom context handler in your web.config.
  4. For IIS6 support or visual studio build-in webserver: add WebSupportModule to system.web section.
  5. For IIS7 support: Add WebSupportModule to system.webserver section.

web.config:

<configSections>
    <sectionGroup name="spring">
        <section name="context" type="YourNamspace.MvcWebContextHandler, YourAssembly"/>    
        ....
    </sectionGroup>    
    ....
</configSections>

<!-- IIS6 -->
<system.web>
    <httpModules>
        <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
    </httpModules>
</system.web>

<!-- IIS7 -->
<system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true" >
        <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
    </modules>
</system.webServer>

Custom application context class:

public class MvcWebApplicationContext: MvcApplicationContext {

    public MvcWebApplicationContext(string name, bool caseSensitive, params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(name, null, configurationLocations, null, caseSensitive))
    { }

    public MvcWebApplicationContext(string name, bool caseSensitive, IApplicationContext parentContext, params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(name, parentContext, configurationLocations, null, caseSensitive))
    { }

    public MvcWebApplicationContext(MvcApplicationContextArgs args)
    : base(args)
    { }

    public MvcWebApplicationContext(string name, bool caseSensitive, string[] configurationLocations, IResource[] configurationResources)
    : this(new MvcApplicationContextArgs(name, null, configurationLocations, configurationResources, caseSensitive))
    { }

    public MvcWebApplicationContext(params string[] configurationLocations)
    : this(new MvcApplicationContextArgs(string.Empty, null, configurationLocations, null, false))
    { }

    protected override XmlObjectDefinitionReader CreateXmlObjectDefinitionReader(DefaultListableObjectFactory objectFactory)
    {
        return new WebObjectDefinitionReader(GetContextPathWithTrailingSlash(), objectFactory, new XmlUrlResolver());
    }

    protected override DefaultListableObjectFactory CreateObjectFactory()
    {
        return new WebObjectFactory(GetContextPathWithTrailingSlash(), IsCaseSensitive);
    }

    private string GetContextPathWithTrailingSlash()
    {
        string contextPath = this.Name;
        if (contextPath == DefaultRootContextName)
        {
            contextPath = "/";
        }
        else 
        {
            contextPath = contextPath + "/";
        }
        return contextPath;
    }
}

Custom context handler class:

public class MvcWebContextHandler : MvcContextHandler {

    protected override Type DefaultApplicationContextType
    {
        get { return typeof(MvcWebApplicationContext); }
    }  
}

We added this bug to Spring.NET's issue tracker:
https://jira.springsource.org/browse/SPRNET-1450

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文