Exposing richtext property from content in Umbraco Web Api
Problem when exposing richtext property from Umbraco through Web Api
Almost every content page in your website will have one richtext property for adding rich content for the page.
This allows you to add Macros so you can render dynamic content inside page content at custom places in the page itself.
The problem begins when you want to expose your content through REST Web Api service (check reference links for full explanation how to implement web api in Umbraco).
Everything will work fine until you try to expose content which contains Macros in itself. If this is the case you will end up with the following exception
<Error><Message>An error has occurred.</Message><ExceptionMessage>Cannot render a macro when UmbracoContext.PageId is null.</ExceptionMessage><ExceptionType>System.InvalidOperationException</ExceptionType><StackTrace> at Umbraco.Web.UmbracoHelper.RenderMacro(String alias, IDictionary`2 parameters) at Umbraco.Web.PropertyEditors.RteMacroRenderingPropertyEditorValueConverter.<>c__DisplayClass4.<ConvertPropertyValue>b__1(String macroAlias, Dictionary`2 macroAttributes) at Umbraco.Core.Macros.MacroTagParser.ParseMacros(String text, Action`1 textFoundCallback, Action`2 macroFoundCallback) at Umbraco.Web.PropertyEditors.RteMacroRenderingPropertyEditorValueConverter.ConvertPropertyValue(Object value) at Umbraco.Core.PublishedContentHelper.<>c__DisplayClassa.<ConvertPropertyValue>b__6(IPropertyEditorValueConverter p) at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext() at Umbraco.Core.PublishedContentHelper.ConvertPropertyValue(Object currentValue, Guid dataType, String docTypeAlias, String propertyTypeAlias) at Umbraco.Web.PublishedContentExtensions.GetPropertyValue(IPublishedContent doc, String alias, Boolean recursive) at Umbraco.Cms.Custom.Controllers.RssFeedController.GetFeed(Int32 id) in d:\MySandbox\playground\Umbraco.Cms.Custom\Controllers\RssFeedController.cs:line 34 at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4() at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)</StackTrace></Error>
The problem is that Umbraco surface controller tries to render macro from controller but it is not aware of the content page. This happen to me when I started to write RSS feed for this blog of mine.
The following action in controller was keep crashing although everything looked pretty much as it should be.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Http; using System.ServiceModel.Syndication; using Umbraco.Core; using Umbraco.Web; using Umbraco.Core.Models; using Umbraco.Web.WebApi; using Umbraco.Cms.Custom.Extensions; using Umbraco.Cms.Custom.Common; namespace Umbraco.Cms.Custom.Controllers { public class RssFeedController:UmbracoApiController { [HttpGet] public Rss20FeedFormatter GetFeed(int id) { SyndicationFeed feed = new SyndicationFeed(); IPublishedContent articleContainer = Umbraco.TypedContent(id); if (articleContainer == null) { articleContainer = Umbraco.TypedContentAtRoot().First(); } if (articleContainer!=null) { List<SyndicationItem> items = new List<SyndicationItem>(); foreach (IPublishedContent article in articleContainer.Descendants(5).Where(a => a.DocumentTypeAlias == "Article")) { items.Add(new SyndicationItem(){ Id = article.Id.ToString(), PublishDate = article.CreateDate, Title = new TextSyndicationContent(article.GetPropertyValue("title").ToString()), Content = new TextSyndicationContent(article.GetPropertyValue("content").ToString()), LastUpdatedTime = article.CreateDate }); } feed.Items = items; } return new Rss20FeedFormatter(feed); } } }
Then I realized that I need somehow to get rid of macro tags from page content. First I was thinking about regular expression, but I noticed that there are differences whether you access property from page itself or from property and I created the following extension for IPublished content interface objects.
public static string GetPropertyUnrenderedValueAsString(this IPublishedContent page, string propertyName) { string result = string.Empty; IPublishedContentProperty prop = page.Properties.Where(p => p.Alias == propertyName).FirstOrDefault(); if (prop != null && prop.Value != null) { result = prop.Value.ToString(); } return result; }
So instead of using GetPropertyValue I used this extension method and it worked fine.
The next thing was to reduce the amount of text in RSS feed, but that method will be described in my next article where I'm going to describe the whole approach for exposing RSS through Web Api in Umbraco and some useful caching for performance boost.
References
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article