ResolveClientUrl 在 ASP.Net 4 和 3.5 中的工作方式不同

发布于 2024-11-09 09:01:58 字数 3515 浏览 0 评论 0原文

[如果我的问题标题没有准确描述我的问题,我深表歉意 - 如果您能想到更好的标题并有权更改它,那么请随意更改它!]。

我认为我偶然发现了 ASP.Net 3.5 和 4.0 之间的一个微小的重大变化。

[编辑:我已经确认 twix 3.5 和 4.0 的行为发生了变化 - 请参阅我的答案]

这是场景: -

我有一个 ASP.Net 3.5 Web 应用程序。 我有一个简单的用户控件 {appRoot}/Controls/Widgets/MyPictureAndTextWidget.ascx ,它本质上包含一些文本和另一个用户控件 ({appRoot}/Controls/Widgets/MyPicture.ascx)。

大多数情况下,该控件以正常方式使用 - 即,将其包含在其他页面的标记中,但我有一个实例,我需要获取 HTML 以使用 Ajax 在客户端上呈现。

我实现这一目标的方法是编写一个 asmx Web 服务,以编程方式创建一个新页面并动态地“LoadControl”用户控件,然后捕获字符串生成器中页面呈现的输出 - 特别不优雅,但它有效!来源见底部。

然而,将项目升级到Asp.Net 4.0后,上述代码不再像以前那样工作;渲染时的图像具有 src="../images/xxx.png (注意不需要的 '../')。

我创建了一个小演示应用程序 http://cid-916198839f3e806c.office.live.com/self.aspx/Public/ TestingImageWTF.zip 如果您想自己运行它,当您使用 3.5 编译该应用程序时,它可以工作(即您在测试页面上看到 2 张蜘蛛的图片),但是当您在 4.0 下编译并运行时,您会发现它是错误的。只看到 1 个蜘蛛(另一张图像的 URL 错误)。

我能想到的唯一解释是 ResolveClientUrl 方法(图像控件将使用该方法来计算出什么是蜘蛛)。当前执行页面到图像的相对路径)的行为不同,图像 url 显示为“../images/xxx.png”,这意味着图像控件“认为”它正在一个页面中执行。在 4.0 下运行时具有类似“{appRoot}/folder/handler”的路径,但在 3.5 下它认为它在上下文“{appRoot}/handler”中运行。

我希望这对您来说有意义 - 抱歉,如果我没有非常清楚或简洁地描述问题。

谁能告诉我们如何: -

  • 恢复 3.5 行为(显然不会恢复到 3.5 框架!)

  • 或者首先在 Web 服务中生成 HTML 的更好方法是什么?

源代码

完整的测试应用程序可以从这里下载 http:// cid-916198839f3e806c.office.live.com/self.aspx/Public/TestingImageWTF.zip

Web服务

    [WebMethod]
    [ScriptMethod]
    public string GetWidgetHtml(int number)
    {
        var pageHolder = new Page
                             {
                                         //AppRelativeVirtualPath = "~/" // I tried playing with this but it made no difference!
                             };
        for (int i = 0; i < number; i++)
        {
            var viewControl = (MyPictureAndTextWidget) pageHolder.LoadControl(@"~/Controls/Widgets/MyPictureAndTextWidget.ascx");
            pageHolder.Controls.Add(viewControl);
        }

        var output = new StringWriter();

        HttpContext.Current.Server.Execute(pageHolder, output, false);

        StringBuilder sb = output.GetStringBuilder();
        string fulloutput = sb.ToString();
        return fulloutput;
    }

以下是我的用户控件的内容

Controls/Widgets/MyPictureAndTextWidget.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyPictureAndTextWidget.ascx.cs" Inherits="TestingImageWTF.Controls.Widgets.MyPictureAndTextWidget" %>
    <%@ Register TagName="Picture" TagPrefix="widget" Src="~/Controls/Widgets/MyPictureWidget.ascx" %>

    <div style="background:#EEEEEE; border:1px dashed;">
        <h4>My control</h4>
        Some text from the widget ....: 
        <br /><widget:Picture runat="server" />
    </div>

Controls/Widgets/MyPictureWidget.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyPictureWidget.ascx.cs" Inherits="TestingImageWTF.Controls.Widgets.MyWidget" %>

    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            image.ImageUrl = "~/images/spider.png";
        }
    </script>
    <asp:Image ID="image" runat="server" />

[Apologies if my question title does not accurately describe my problem- if you can think of a better title and have the permissions to change this then please feel free to change it!].

I think that I have stumbled upon a minor breaking change between ASP.Net 3.5 and 4.0.

[Edit: I have confirmed that there is a change in behaviour twix 3.5 and 4.0 - see my answer]

Here is the scenario: -

I have a ASP.Net 3.5 web application.
I have a trivial user control {appRoot}/Controls/Widgets/MyPictureAndTextWidget.ascx that essentially contains some text and another user control ({appRoot}/Controls/Widgets/MyPicture.ascx).

For the most part, this control is used in the normal fashion - i.e. including it in the mark up of other pages but I have one instance where I need to obtain the HTML to render on the client using Ajax.

The way I achieved this was to write an asmx web service that programmatically created a new Page and dynamically `LoadControl' the user controls and then captured the output from the rendering of the page in a string builder - particulary inelegant but it worked! See bottom for the source.

However, after upgrading the project to Asp.Net 4.0, the above code no longer works as it used to; the image, when rendered has src="../images/xxx.png (note the '../' which is not wanted).

I have created a little demo app http://cid-916198839f3e806c.office.live.com/self.aspx/Public/TestingImageWTF.zip if you want to run it for yourselves. When you compile the app using 3.5, it works (i.e. you see 2 pictures of a spider on the test page) but when you compile and run under 4.0, you only see 1 spider (the other image has the wrong URL).

The only explanation that I can come up with is that the ResolveClientUrl method (which the Image control will use in order to work out what is the relative path to the image from the currently executing page) is behaving differently. The fact that the image url is coming out as "../images/xxx.png" means that the Image control 'thinks' it is executing in a page that has a path like '{appRoot}/folder/handler' when running under 4.0 but it thinks it is running in a context '{appRoot}/handler' under 3.5.

I hope this is making sense to you - sorry if I am not describing the problem very clearly or concisely.

Can anyone either tell us how: -

  • to restore the 3.5 behaviour (without reverting to the 3.5 framework obviously!)

  • or a better way of generating the HTML in the web service in the first place?

The source

A full test application can be downloaded from here http://cid-916198839f3e806c.office.live.com/self.aspx/Public/TestingImageWTF.zip

Web Service

    [WebMethod]
    [ScriptMethod]
    public string GetWidgetHtml(int number)
    {
        var pageHolder = new Page
                             {
                                         //AppRelativeVirtualPath = "~/" // I tried playing with this but it made no difference!
                             };
        for (int i = 0; i < number; i++)
        {
            var viewControl = (MyPictureAndTextWidget) pageHolder.LoadControl(@"~/Controls/Widgets/MyPictureAndTextWidget.ascx");
            pageHolder.Controls.Add(viewControl);
        }

        var output = new StringWriter();

        HttpContext.Current.Server.Execute(pageHolder, output, false);

        StringBuilder sb = output.GetStringBuilder();
        string fulloutput = sb.ToString();
        return fulloutput;
    }

Here are the contents of my user controls

Controls/Widgets/MyPictureAndTextWidget.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyPictureAndTextWidget.ascx.cs" Inherits="TestingImageWTF.Controls.Widgets.MyPictureAndTextWidget" %>
    <%@ Register TagName="Picture" TagPrefix="widget" Src="~/Controls/Widgets/MyPictureWidget.ascx" %>

    <div style="background:#EEEEEE; border:1px dashed;">
        <h4>My control</h4>
        Some text from the widget ....: 
        <br /><widget:Picture runat="server" />
    </div>

Controls/Widgets/MyPictureWidget.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyPictureWidget.ascx.cs" Inherits="TestingImageWTF.Controls.Widgets.MyWidget" %>

    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            image.ImageUrl = "~/images/spider.png";
        }
    </script>
    <asp:Image ID="image" runat="server" />

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

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

发布评论

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

评论(3

但可醉心 2024-11-16 09:01:58

o所以这至少是答案的一部分。

问题:ResolveClientUrl 在 ASP.Net 4 和 3.5 中的工作方式是否不同?

答案:

行为的变化(据我所知)是它以不同的方式对待 PathInfo。

为了进行演示,请制作以下页面。

<%@ Page Language="C#" AutoEventWireup="true"  %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
    <form id="form1" runat="server">
        DateTime.Now.Ticks: <%= DateTime.Now.Ticks %>
        <br />
        <asp:HyperLink runat="server" NavigateUrl="~/PathInfoLinkTest.aspx">This links to ~/PathInfoLinkTest.aspx</asp:HyperLink>
        <br />
        <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/PathInfoLinkTest.aspx/foo/bar">This links to ~/PathInfoLinkTest.aspx/foo/bar</asp:HyperLink>
        <br />
        ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = <%= ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") %>
    </form>
</body>
</html>

并在.Net4和.Net 3.5下运行。

您将在 3.5 下看到:
ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = 'PathInfoLinkTest.aspx/foo/bar'

而在 4.0 下您会得到
ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = 'bar'

此更改似乎是针对这些人遇到的问题进行的错误修复。

本质上,3.5 中的错误是,如果您当前浏览网址 http://host/app/page.aspx/foo/bar 并且您想要链接到 http://host/app/page2.aspx,那么客户端呈现的 URL 应该是 ../../page2.aspx

Asp.Net 4 正确!
Asp.Net 3.5 不正确 - 它将链接的 url 输出为“page2.aspx”(因此当单击时,浏览器将请求页面“< code>http://host/app/page.aspx/foo/bar/page2.aspx' 如果您在 .Net 3.5 中运行上述页面并单击第二个超链接几次 - 然后查看浏览器的地址栏!

不幸的是,错误修复破坏了我的代码 - 因为我的代码依赖于 .Net 3.5 的(不正确)行为:Web 服务请求始终具有 Pathinfo(Web 服务)方法名称),因此当控件呈现自身时,调用 ResolveClientUrl("~/xxx") (正确)会返回“../xxx”。

oSo here is at least part if the answer.

Question: Does ResolveClientUrl working differently in ASP.Net 4 and 3.5?

Answer: Yes.

And the change in behaviour (that I know of) is that it treats PathInfo differently.

To demonstrate, make the following page.

<%@ Page Language="C#" AutoEventWireup="true"  %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
    <form id="form1" runat="server">
        DateTime.Now.Ticks: <%= DateTime.Now.Ticks %>
        <br />
        <asp:HyperLink runat="server" NavigateUrl="~/PathInfoLinkTest.aspx">This links to ~/PathInfoLinkTest.aspx</asp:HyperLink>
        <br />
        <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/PathInfoLinkTest.aspx/foo/bar">This links to ~/PathInfoLinkTest.aspx/foo/bar</asp:HyperLink>
        <br />
        ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = <%= ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") %>
    </form>
</body>
</html>

And run under .Net4 and .Net 3.5.

You will see that under 3.5:
ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = 'PathInfoLinkTest.aspx/foo/bar'

whereas under 4.0 you get
ResolveClientUrl("~/PathInfoLinkTest.aspx/foo/bar") = 'bar'

The change seems to be a bug fix in response to the problems that these folk were having.

In essence, the bug in 3.5 is that if you are currently browsing the url http://host/app/page.aspx/foo/bar and you want to link to http://host/app/page2.aspx, then the URL as rendered on the client should be ../../page2.aspx.

Asp.Net 4 gets this correct!
Asp.Net 3.5 doesn't - it outputs the link's url as 'page2.aspx' (so when clicked, the browser will request the page 'http://host/app/page.aspx/foo/bar/page2.aspx'. You can see a manifestation of this bug if you run the above page in .Net 3.5 and click on the 2nd hyperlink several times - then have a look in your browser's address bar!

Unfortunately the bug fix broke my code - because my code was relying on the (incorrect) behaviour of .Net 3.5: The web service request always has Pathinfo (the web service method name) and so when the controls render themselves, calls to ResolveClientUrl("~/xxx") (correctly) puts returns "../xxx".

北方的韩爷 2024-11-16 09:01:58

我不确定 ResolveClientUrl 方法是否更新,但我确实知道他们对 .NET 4.0 和 3.5 之间控件的呈现方式进行了更新。您可能需要尝试更新您的 web.config 以包括:

<pages controlRenderingCompatibilityVersion="3.5" />

查看: http://www.asp.net/learn/whitepapers/aspnet4/writing-changes#0.1__Toc256770141

另外,您可以尝试在 Web 服务中按如下方式使用 RenderControl:

StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
HtmlTextWriter hw = new HtmlTextWriter(tw);

control.RenderControl(hw);
return sb.ToString();

Rick Strahl 有一篇可能有用的文章: http://www.west -wind.com/weblog/posts/2004/Jun/08/Capturing-Output-from-ASPNet-Pages(但是可能有点过时......)

希望这有帮助!

I'm not sure about updates to the ResolveClientUrl method, but I do know that they made updates to how controls are rendered between .NET 4.0 and 3.5. You may want to try updating your web.config to include:

<pages controlRenderingCompatibilityVersion="3.5" />

Check out: http://www.asp.net/learn/whitepapers/aspnet4/breaking-changes#0.1__Toc256770141

Also, you might try using RenderControl as follows in your web service:

StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
HtmlTextWriter hw = new HtmlTextWriter(tw);

control.RenderControl(hw);
return sb.ToString();

Rick Strahl has an article that might be useful: http://www.west-wind.com/weblog/posts/2004/Jun/08/Capturing-Output-from-ASPNet-Pages (may be somewhat dated however...)

Hope this helps!

十六岁半 2024-11-16 09:01:58

尝试从 ImageUrl 值中删除前导波浪号符号。

Try to remove the leading tilde symbol from the ImageUrl value.

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