Frage

Ich habe gestern ein Upgrade auf MVC 4 durchgeführt und gerade einen Fehler entdeckt, der durch das Upgrade verursacht wurde.

Ich habe eine Razor-Ansicht, die zum Generieren eines RSS-Feeds verwendet wird.Es hat ein Markup wie dieses (vereinfacht):

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

In Razor Version zwei gibt es das besondere Unterstützung für Leere HTML5-Elemente.Solche void-Elemente schließen sich selbst und haben kein schließendes Tag.

Bedauerlicherweise, <link> ist ein solches Element.

Dies bedeutet, dass das obige Razor-Markup nicht mehr gültig ist und zur Laufzeit fehlschlägt.Entfernen des Verschlusses </link> -Tag entfernt den Parser-Fehler, bedeutet aber, dass es sich nicht mehr um ein gültiges RSS handelt.

Gibt es also eine Möglichkeit, dies zu umgehen, oder ist Razor eigentlich nur für die Generierung von HTML5 geeignet?

War es hilfreich?

Lösung 4

Die kurze Antwort auf diese Frage scheint zu sein, dass Razor, wie auch Version 2, an HTML gebunden ist und XML ausschließt.ICH habe einen der Entwickler um Bestätigung gebeten, also kommt er hoffentlich zurück.

Am Ende habe ich meine Methode geändert, um Linq zu XML und eine benutzerdefinierte Methode zu verwenden ActionResult, unter Umgehung von Razor und tatsächlich jeder Ansichts-Engine:

[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;
}

Dies erfordert den folgenden Brauch 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);
    }
}

Andere Tipps

Ich würde es so tun:

generasacodicetagpre.

erzeugte Quelle sehen so aus:

generasacodicetagpre.

Für jetzt gehe ich mit dieser Problemumgehung:

generasacodicetagpre.

Da Alexander Taran auf der Suche nach einer endgültigen Antwort darauf ein Kopfgeld auf diese Frage eröffnet hat, dachte ich, ich schaue mir das mal an Razor-Quellcode auf CodePlex und geben Sie einige Details an.

Werfen Sie zunächst einen Blick darauf HtmlMarkupParser.Es enthält diese Referenzdaten:

//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"
};

Dies wird über angezeigt HtmlMarkupParser.VoidElements, und die einzige Nutzung dieser Eigenschaft ist in HtmlMarkupParser.RestOfTag(...).Dies ist ein Parser, der eine Folge von Token durchläuft.Der relevante Codeausschnitt ist:

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();
}

Das bedeutet, dass Folgendes erfolgreich geparst wird:

<link></link>

Der Lookahead ist jedoch begrenzt, was bedeutet, dass alle zusätzlichen Token, die vor dem schließenden Tag gesehen werden, zum Scheitern führen:

<link>Some other tokens</link>

In diesem Fall kann es möglich sein, die Reichweite des Lookheads zu erweitern.Wenn jemand Interesse hat, kann er dem MVC-Team eine Pull-Anfrage stellen.

Der HTML5-Link ist ein spezielles Element, das im Header für Stylesheets und dergleichen verwendet wird.

Ihr RSS sollte nicht Html5 sein, sondern so etwas wie

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

Sie könnten dies in einem Layout-Controller haben, den Ihre RSS-Feeds verwenden würden

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

Eine alternative Möglichkeit, die ich zuvor gemacht habe, besteht darin, eine völlig leere Ansicht und dann den folgenden Controller zu haben:

    [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);
    }

Besonders hervorzuheben ist System.ServiceModel.Syndication.SyndicationFeed

Und das ist mein individuelles Ergebnis

 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);
            }
        }
    }

was Sie tun können, ist dies:

generasacodicetagpre.

viel einfacher

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top