12

昨日 MVC 4 にアップグレードしたところ、アップグレードによって発生したバグが発見されました。

RSS フィードの生成に使用される Razor ビューがあります。次のようなマークアップがあります (簡略化)。

<item>
    <title>@post.BlogPost.Title</title> 
    <link>@Url.BlogPost(post.BlogPost, isAbsolute: true)</link>
</item>

Razor バージョン 2 では、HTML5 void 要素が特別にサポートされています。このような void 要素は自己終了であり、終了タグはありません。

残念ながら、<link>そのような要素の 1 つです。

これは、上記の Razor マークアップが無効になり、実行時に失敗することを意味します。終了</link>タグを削除すると、パーサー エラーは削除されますが、有効な RSS ではなくなります。

それで、これを回避する方法はありますか、それとも Razor は HTML5 の生成にのみ適していますか?

4

6 に答える 6

12

私は次のようにします:

<item>
   <title>
      @post.BlogPost.Title
   </title>

   @Html.Raw("<link>")
      @Url.BlogPost(post.BlogPost, isAbsolute: true)
   @Html.Raw("</link>")
</item>

生成されたソースは次のようになります。

<item>
    <title>
        Google
    </title>

     <link>
         http://www.google.se
    </link>
</item>
于 2012-12-17T11:09:54.347 に答える
2

アレクサンダータランはこれに対する決定的な答えを探してこの質問に報奨金を開いたので、CodePlexのRazorソースコードをチェックして詳細を提供したいと思いました。

まず、を見てくださいHtmlMarkupParser。これには、次の参照データが含まれています。

//From http://dev.w3.org/html5/spec/Overview.html#elements-0
private ISet<string> _voidElements = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
    "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen",
    "link", "meta", "param", "source", "track", "wbr"
};

これはを介して公開されHtmlMarkupParser.VoidElements、このプロパティの唯一の使用法はにありHtmlMarkupParser.RestOfTag(...)ます。これは、一連のトークンをウォークスルーするパーサーです。関連するコードスニペットは次のとおりです。

if (VoidElements.Contains(tagName))
{
    // Technically, void elements like "meta" are not allowed to have end tags. Just in case they do,
    // we need to look ahead at the next set of tokens. If we see "<", "/", tag name, accept it and the ">" following it
    // Place a bookmark
    int bookmark = CurrentLocation.AbsoluteIndex;

    // Skip whitespace
    IEnumerable<HtmlSymbol> ws = ReadWhile(IsSpacingToken(includeNewLines: true));

    // Open Angle
    if (At(HtmlSymbolType.OpenAngle) && NextIs(HtmlSymbolType.Solidus))
    {
        HtmlSymbol openAngle = CurrentSymbol;
        NextToken();
        Assert(HtmlSymbolType.Solidus);
        HtmlSymbol solidus = CurrentSymbol;
        NextToken();
        if (At(HtmlSymbolType.Text) && String.Equals(CurrentSymbol.Content, tagName, StringComparison.OrdinalIgnoreCase))
        {
            // Accept up to here
            Accept(ws);
            Accept(openAngle);
            Accept(solidus);
            AcceptAndMoveNext();

            // Accept to '>', '<' or EOF
            AcceptUntil(HtmlSymbolType.CloseAngle, HtmlSymbolType.OpenAngle);
            // Accept the '>' if we saw it. And if we do see it, we're complete
            return Optional(HtmlSymbolType.CloseAngle);
        } // At(HtmlSymbolType.Text) && String.Equals(CurrentSymbol.Content, tagName, StringComparison.OrdinalIgnoreCase)
    } // At(HtmlSymbolType.OpenAngle) && NextIs(HtmlSymbolType.Solidus)

    // Go back to the bookmark and just finish this tag at the close angle
    Context.Source.Position = bookmark;
    NextToken();
}

これは、以下が正常に解析されることを意味します。

<link></link>

ただし、先読みは制限されています。つまり、終了タグの前に余分なトークンが表示されると、失敗します。

<link>Some other tokens</link>

この場合、先読みの範囲を拡大できる可能性があります。誰かが熱心であれば、MVCチームにプルリクエストを提供できます。

于 2012-12-12T11:15:09.903 に答える
2

今のところ、この回避策を使用します。

 @Html.Raw(string.Format(@"<param name=""{0}"">{1}</param>",Name, Value)) 
于 2012-12-12T11:06:07.837 に答える
1

この質問に対する簡単な答えは、Razor はバージョン 2 以降と同様に、XML を除外して HTML に関連付けられているようです。私は開発者の一人に確認を求めたので、彼が戻ってくることを願っています.

最終的に、Linq を XML とカスタム に使用する方法を変更し、ActionResultRazor と実際にはすべてのビュー エンジンをバイパスしました。

[HttpGet]
[OutputCache(Duration = 300)]
public ActionResult Feed()
{
    var result = new XmlActionResult(
        new XDocument(
            new XElement("rss",
                new XAttribute("version", "2.0"),
                new XElement("channel",
                    new XElement("title", "My Blog")
                    // snip
                )
            )
        )
    );

    result.MimeType = "application/rss+xml";

    return result;
}

これには、次のカスタムが必要ActionResultです。

public sealed class XmlActionResult : ActionResult
{
    private readonly XDocument _document;

    public Formatting Formatting { get; set; }
    public string MimeType { get; set; }

    public XmlActionResult([NotNull] XDocument document)
    {
        if (document == null)
            throw new ArgumentNullException("document");

        _document = document;

        // Default values
        MimeType = "text/xml";
        Formatting = Formatting.None;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.Clear();
        context.HttpContext.Response.ContentType = MimeType;

        using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting })
            _document.WriteTo(writer);
    }
}
于 2012-10-04T10:28:10.763 に答える
1

Html5 リンクは、スタイルシートなどのヘッダーで使用される特別な要素です。

Rss は Html5 ではなく、次のようなものにする必要があります

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

これは、RSS フィードが使用するレイアウト コントローラーに含めることができます。

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
    @RenderBody()
</rss>

以前に行った別の方法は、完全に空のビューを作成してから、以下のコントローラーを作成することです。

    [NHibernateActionFilter]
    public AtomActionResult Feed()
    {
        var dto = _service.GetThings(NHibernateSession);
        var items = Mapper.Map<List<ThingDto>, List<SyndicationItem>>(dto);
        var url = HttpContextWrapper.Request.UrlReferrer;
        var feed = new SyndicationFeed("MyTitle", "MyByline", url, items)
        {
            Copyright = new TextSyndicationContent("© 2012 SO"),
            Language = "en-IE"
        };
        return new AtomActionResult(feed);
    }

特筆すべきは、System.ServiceModel.Syndication.SyndicationFeed

そして、これが私のカスタム結果です

 public class AtomActionResult : ActionResult
    {
        readonly SyndicationFeed _feed;

        public AtomActionResult() { }

        public AtomActionResult(SyndicationFeed feed)
        {
            _feed = feed;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            //context.HttpContext.Response.ContentType = "application/atom+xml";
            //chrome does not yet support atom+xml 
            //http://code.google.com/p/chromium/issues/detail?id=104358
            context.HttpContext.Response.ContentType = "application/xml";
            var formatter = new Atom10FeedFormatter(_feed);
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                formatter.WriteTo(writer);
            }
        }
    }
于 2012-10-03T15:25:39.503 に答える
0

あなたができることはこれです:

@("<link>" + Url.BlogPost(post.BlogPost, isAbsolute: true) + "</link>")

はるかに簡単

于 2013-06-26T14:23:08.977 に答える