7

I have created some custom facets where I store some data. I would like to display this data on the User Profile but I am running into issues.

I am using this link as a guide to set this up: http://reyrahadian.com/2016/08/27/sitecore-8-extending-experience-profile-part-2-displaying-contact-facet-simple-properties/

My HttpRouteConfig.cs file looks like:

using System.Web.Http;
using System.Web.Routing;
using Sitecore.Pipelines;

namespace MySite.Presentation
{
    public class HttpRouteConfig
    {
        public virtual void Process(PipelineArgs args)
        {
            RegisterRoutes(RouteTable.Routes, args);
        }

        protected virtual void RegisterRoutes(RouteCollection routes, PipelineArgs args)
        {
            routes.MapHttpRoute("customcontact_customfields", "sitecore/api/ao/v1/contacts/{contactId}/customfields", (object)new
            {
                controller = "CustomFields",
                action = "GetCustomFields"
            });
        }
    }
}

My controller looks like:

using MySite.Models.Utility;
using Sitecore.Analytics.Tracking;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Cintel.ContactService;
using Sitecore.Cintel.Endpoint.Plumbing;
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MySite.Presentation.Controllers
{

    [AuthorizedReportingUserFilter]
    public class CustomFieldsController : ApiController
    {
        [HttpGet]
        public object GetCustomFields(Guid contactId)
        {
            try
            {
                var contactManager = GetContactManager();
                var contact = contactManager.LoadContactReadOnly(contactId);
                if (contact == null)
                {
                    Log.Info("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields: Contact not found!", this);
                    throw new ContactNotFoundException();
                }
                var customFacet = contact.GetFacet<IContactCompanyInfo>(ContactCompanyInfo.FACET_NAME);
                Log.Info("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields: Company: " + customFacet.Company + " Industry: " + customFacet.Industry + " Sub Industry: " + customFacet.SubIndustry + " Revenue Range: " + customFacet.RevenueRange, this);
                return customFacet;
            }
            catch (ContactNotFoundException ex)
            {
                Log.Error("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields", ex, this);
                return Request.CreateResponse(HttpStatusCode.NotFound, ex.Message);
            }
        }

        private static ContactManager GetContactManager()
        {
            var contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;
            Assert.IsNotNull(contactManager, "Could not create instance of ContactManager");
            return contactManager;
        }
    }
}

My Javascript file looks like:

define(["sitecore", "/-/speak/v1/experienceprofile/DataProviderHelper.js", "/-/speak/v1/experienceprofile/CintelUtl.js"], function (sc, providerHelper, cintelUtil) {
    var app = sc.Definitions.App.extend({
        initialized: function () {
            var localUrl = "/customfields/";

            providerHelper.setupHeaders([
                { urlKey: localUrl }
            ]);

            var url = sc.Contact.baseUrl + localUrl;
            var $that = this;

            providerHelper.initProvider(this.AdditionalInfoDataProvider, "", url, this.AdditionalInfoTabMessageBar);
            providerHelper.getData(this.AdditionalInfoDataProvider,
                $.proxy(function (jsondata) {
                    cintelUtil.setText($that.CompanyValue, jsondata.Company, true);
                    cintelUtil.setText($that.IndustryValue, jsondata.Industry, true);
                    cintelUtil.setText($that.SubIndustryValue, jsondata.SubIndustry, true);
                    cintelUtil.setText($that.RevenueRangeValue, jsondata.RevenueRange, true);
                }));
        }
    });
    return app;
});

When I try to reference: https://stage.mysite.com/sitecore/api/ao/v1/contacts/3187658b-b035-4d2e-b348-7e98aed90990/customfields

I get the super-helpful response of:

{ "Message": "An error has occurred." }

You might note that I have some logging in my controller. I can tell that I am at least getting the controller to call because I see the Company Name, etc. written into the logs when I call the above URL.

Any ideas?

I am running Sitecore 8.1 Update 2.


Update: Per suggestion, I turned off customErrors and now I get:

{ "Message": "An error has occurred.", "ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json'.", "ExceptionType": "System.InvalidOperationException", "StackTrace": null, "InnerException": { "Message": "An error has occurred.", "ExceptionMessage": "Self referencing loop detected for property 'parent' with type 'MySite.Models.Utility.ContactCompanyInfo'. Path 'members[0]'.", "ExceptionType": "Newtonsoft.Json.JsonSerializationException", "StackTrace": " at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()" } }


Update 2: This is what my facet looks like:

using Sitecore.Analytics.Model.Framework;
using System;

namespace MySite.Models.Utility
{
    public interface IContactCompanyInfo : IFacet
    {
        string Company { get; set; }
        string RevenueRange { get; set; }
        string Industry { get; set; }
        string SubIndustry { get; set; }
    }

    [Serializable]
    public class ContactCompanyInfo : Facet, IContactCompanyInfo
    {
        public const string FACET_NAME = "Company Info";
        private const string FIELD_COMPANY = "Company";
        private const string FIELD_INDUSTRY = "Industry";
        private const string FIELD_SUB_INDUSTRY = "SubIndustry";
        private const string FIELD_REVENUE_RANGE = "RevenueRange";

        public ContactCompanyInfo()
        {
            EnsureAttribute<string>(FIELD_COMPANY);
            EnsureAttribute<string>(FIELD_INDUSTRY);
            EnsureAttribute<string>(FIELD_SUB_INDUSTRY);
            EnsureAttribute<string>(FIELD_REVENUE_RANGE);
        }
        public string Company
        {
            get { return GetAttribute<string>(FIELD_COMPANY); }
            set { SetAttribute(FIELD_COMPANY, value); }
        }

        public string Industry
        {
            get { return GetAttribute<string>(FIELD_INDUSTRY); }
            set { SetAttribute(FIELD_INDUSTRY, value); }
        }

        public string SubIndustry
        {
            get { return GetAttribute<string>(FIELD_SUB_INDUSTRY); }
            set { SetAttribute(FIELD_SUB_INDUSTRY, value); }
        }

        public string RevenueRange
        {
            get { return GetAttribute<string>(FIELD_REVENUE_RANGE); }
            set { SetAttribute(FIELD_REVENUE_RANGE, value); }
        }
    }
}
  • I don't think the route config is needed. Instead, I believe you want to add the controller to the list of "allowedControllers" in the <sitecore><api><services> node. – Pete Navarra Oct 11 '16 at 17:03
  • 1
    Do you have customErrors turned on? Setting it to off should give you a more detailed error message. – Geoff Warren Oct 11 '16 at 17:06
  • @GeoffWarren Hmm interesting... turned it off and it throws the error shown in my update above. – eat-sleep-code Oct 11 '16 at 17:32
  • @eat-sleep-code - Can you post your Facet concrete class? and IElement concrete class? – Pete Navarra Oct 11 '16 at 18:42
  • @PeteNavarra you were reading my mind. I just did. – eat-sleep-code Oct 11 '16 at 18:45
1

According to that error you have a self-referencing loop in your facet object that is breaking the serializer.

You can configure JSON.NET to ignore reference loops through serialization settings. http://www.newtonsoft.com/json/help/html/serializationsettings.htm

  • "You could also project the facet to a new object" ... not sure what you mean there. – eat-sleep-code Oct 11 '16 at 18:24
  • I also don't understand how there is a self-referencing loop in the Facet? The facet is pretty simple -- just four pieces of data. – eat-sleep-code Oct 11 '16 at 18:41
  • Forget the projection part, I was being dumb. – Geoff Warren Oct 11 '16 at 19:27
1

I have serialized Facets Values using Newtonsoft.Json and after that, it works fine for me. Here Newtonsoft.Json will serialize and give you a string object so before return, it to the JSON data you need to parse it as below:

try
{
    var contactManager = GetContactManager();
    var contact = contactManager.LoadContactReadOnly(contactId);
    if (contact == null)
    {
        Log.Info("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields: Contact not found!", this);
        throw new ContactNotFoundException();
    }
    var customFacet = contact.GetFacet<IContactCompanyInfo>(ContactCompanyInfo.FACET_NAME);
    Log.Info("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields: Company: " + customFacet.Company + " Industry: " + customFacet.Industry + " Sub Industry: " + customFacet.SubIndustry + " Revenue Range: " + customFacet.RevenueRange, this);
    var serializerSettings = new JsonSerializerSettings
    {
        ContractResolver = new XdbJsonContractResolver(client.Model, serializeFacets: true, serializeContactInteractions: true),
        DateTimeZoneHandling = DateTimeZoneHandling.Utc,
        DefaultValueHandling = DefaultValueHandling.Ignore
    };
    return Newtonsoft.Json.Linq.JObject.Parse(JsonConvert.SerializeObject(customFacet, serializerSettings));
}
catch (ContactNotFoundException ex)
{
    Log.Error("MySite.Presentation.Controllers.CustomFieldsController.GetCustomFields", ex, this);
    return Request.CreateResponse(HttpStatusCode.NotFound, ex.Message);
}

You can find serialization setting more details from here.

0

This magically fixed itself on Friday. I am not really sure why or how, but I am not complaining.

  • I am getting the same issue, do you have any idea how you resolved it? – Himmat Singh Dulawat May 16 '19 at 5:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.