Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Bjarne Fyrstenborg 1280 posts 3990 karma points MVP 7x c-trib
    Mar 23, 2016 @ 08:32
    Bjarne Fyrstenborg
    0

    Return camelCase property names from API controller

    I am running Umbraco 7.4.1 and have a custom property editor to search reviews (listview nodes) and pick some of the result (similar to the MNTP, but not with the whole tree structure and more details for each "Review" node.

    For that purpose I am using an API Controller to return data from the Examine index.

    Model:

    public class Review
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string City { get; set; }
        public DateTime Date { get; set; }
        public string Destination { get; set; }
        public string Header { get; set; }
        public string Hotel { get; set; }
        public int HotelRating { get; set; }
        public int Rating { get; set; }
        public bool ShowComment { get; set; }
        public string Text { get; set; }
        public string Photo { get; set; }
    }
    
    public class ReviewSearchResult
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public string Path { get; set; }
        public Review Review { get; set; }
    }
    

    API Controller

    public class ReviewsController : UmbracoAuthorizedApiController //UmbracoApiController //UmbracoAuthorizedJsonController
    {
                //[System.Web.Http.HttpGet]
                //[CacheOutput(ServerTimeSpan = 86400)]
                public IEnumerable<ReviewSearchResult> GetSearchReviews(int rootId, string keyword, bool onlyCommentsEnabled = false, int limit = 200)
                {
                    string searcherName = GetSearcherName(rootId);
                    var searcher = ExamineManager.Instance.SearchProviderCollection[searcherName];
                    var criteria = searcher.CreateSearchCriteria();
    
                    var results = searcher.Search(criteria.RawQuery(string.Format("nodeName:*{0}* reviewName:*{0}* reviewCity:*{0}* reviewDestination:*{0}* reviewHotel:*{0}*", keyword)));
    
                    List<ReviewSearchResult> reviewResults = new List<ReviewSearchResult>();
                    foreach (var r in results.OrderBy(x => x.Score).Take(limit))
                    {
    
                        bool showComment = r.Fields.ContainsKey("reviewShowComment") ? r.Fields["reviewShowComment"] != "0" : false;
                        if(onlyCommentsEnabled && !showComment)
                        {
                            continue;
                        }
    
                        Review review = new Review
                        {
                            Name = r.Fields.ContainsKey("reviewName") ? r.Fields["reviewName"] : "", //nodeName
                            City = r.Fields.ContainsKey("reviewCity") ? r.Fields["reviewCity"] : "",
                            Date = DateTime.Parse(r.Fields.ContainsKey("reviewDate") ? r.Fields["reviewDate"] : DateTime.Now.ToString()),
                            Destination = r.Fields.ContainsKey("reviewDestination") ? r.Fields["reviewDestination"] : "",
                            Header = r.Fields.ContainsKey("reviewHeader") ? r.Fields["reviewHeader"] : "",
                            Hotel = r.Fields.ContainsKey("reviewHotel") ? r.Fields["reviewHotel"] : "",
                            HotelRating = int.Parse(r.Fields.ContainsKey("reviewHotelRating") ? r.Fields["reviewHotelRating"] : "0"),
                            Rating = int.Parse(r.Fields.ContainsKey("reviewRating") ? r.Fields["reviewRating"] : "0"),
                            Text = r.Fields.ContainsKey("reviewText") ? r.Fields["reviewText"] : "",
                            Photo = r.Fields.ContainsKey("reviewPhoto") ? r.Fields["reviewPhoto"] : "",
                            ShowComment = showComment
                        };
    
                        reviewResults.Add(new ReviewSearchResult
                        {
                            Id = int.Parse(r.Fields.ContainsKey("id") ? r.Fields["id"] : "0"),
                            Name = r.Fields.ContainsKey("nodeName") ? r.Fields["nodeName"] : "",
                            Url = Umbraco.NiceUrl(int.Parse(r.Fields.ContainsKey("id") ? r.Fields["id"] : "0")),
                            Path = r.Fields.ContainsKey("path") ? r.Fields["path"] : "",
                            Review = review
                        });
                    }
    
                    // camelcase properties when it is json response
                    var jsonFormatter = GlobalConfiguration.Configuration.Formatters.OfType<JsonMediaTypeFormatter>().First();
                    var settings = jsonFormatter.SerializerSettings;
                    settings.Formatting = Formatting.Indented;
                    settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    
                    return reviewResults;
                }
    
                ...
    }
    

    When using UmbracoApiController or UmbracoAuthorizedApiController it returns camelCase property names, but with UmbracoAuthorizedJsonController it seems to ignore that part and returns the default casing of the property names.

    How can I use UmbracoAuthorizedJsonController and still return camelCase property names?

    /Bjarne

  • Shannon Deminick 1525 posts 5271 karma points MVP 2x
    Mar 23, 2016 @ 09:19
    Shannon Deminick
    1

    Hi Bjarne,

    There's a few things you'll need to understand in order to know what is going on here. Firstly, there are global formatters and then there are controller specific formatters. By default global formatters will apply to ALL controllers in your solution - but if we allowed for developers to change the formatting of our data to whatever they want then everything would probably just break. Therefore for Umbraco's purposes - we apply controller specific formatters so we know exactly how the data will be formatted & cannot be tampered with so the back office doesn't break.

    Here's what these controllers do:

    So to answer your question, if you need to use UmbracoAuthorizedJsonController because you are creating angular based requests for the back office and you want to apply custom formatting output to that, then you need to apply a controller specific formatter to your controller. Or you can attribute your classes/properties with the correct DataContract bindings to specify the exact names you want.

  • Bjarne Fyrstenborg 1280 posts 3990 karma points MVP 7x c-trib
    Mar 23, 2016 @ 10:17
    Bjarne Fyrstenborg
    0

    Hi Shannon

    Thanks for your detailed explanation of the difference between the two API Controllers.

    I am not quite sure how to apply a controller specific formatter to a controller - do I need to create a formatter similar to this? https://github.com/umbraco/Umbraco-CMS/blob/d50e49ad37fd5ca7bad2fd6e8fc994f3408ae70c/src/Umbraco.Web/WebApi/AngularJsonMediaTypeFormatter.cs

    By adding DataContract attributes for properties and classes, it should be something similar to this, right? https://msdn.microsoft.com/en-us/library/jj870778.aspx

  • Shannon Deminick 1525 posts 5271 karma points MVP 2x
    Mar 23, 2016 @ 10:35
    Shannon Deminick
    1

    If it's for back office use then you absolutely must continue to use the AngularJsonMediaTypeFormatter - you can create a sub-class of this to modify it and apply the CamelCasePropertyNamesContractResolver to it

    using DataContract attribute is as mention. Please have a browse through our source code, there's hundreds of examples of all of this stuff in there.

  • Bjarne Fyrstenborg 1280 posts 3990 karma points MVP 7x c-trib
    Mar 23, 2016 @ 14:30
    Bjarne Fyrstenborg
    1

    Hi Shannon

    Okay, I might take a look at it at some other time. For now I found it easiest just to add [DataContract] and [DataMember] attributes for the model class and properties.

    using System;
    using System.Runtime.Serialization;
    
    namespace Reviews.Models
    {
        [DataContract]
        public class Review
        {
    
        [DataMember(Name = "id", EmitDefaultValue = false)]
        public int Id { get; set; }
    
        [DataMember(Name = "name", EmitDefaultValue = false)]
        public string Name { get; set; }
    
        [DataMember(Name = "city", EmitDefaultValue = false)]
        public string City { get; set; }
    
        [DataMember(Name = "date", EmitDefaultValue = false)]
        public DateTime Date { get; set; }
    
        [DataMember(Name = "destination", EmitDefaultValue = false)]
        public string Destination { get; set; }
    
        [DataMember(Name = "header", EmitDefaultValue = false)]
        public string Header { get; set; }
    
        [DataMember(Name = "hotel", EmitDefaultValue = false)]
        public string Hotel { get; set; }
    
        [DataMember(Name = "hotelRating", EmitDefaultValue = false)]
        public int HotelRating { get; set; }
    
        [DataMember(Name = "rating", EmitDefaultValue = false)]
        public int Rating { get; set; }
    
        [DataMember(Name = "showComment", EmitDefaultValue = false)]
        public bool ShowComment { get; set; }
    
        [DataMember(Name = "text", EmitDefaultValue = false)]
        public string Text { get; set; }
    
        [DataMember(Name = "photo", EmitDefaultValue = false)]
        public string Photo { get; set; }
    }
    
    [DataContract]
    public class ReviewSearchResult
    {
        [DataMember(Name = "id", EmitDefaultValue = false)]
        public int Id { get; set; }
    
        [DataMember(Name = "name", EmitDefaultValue = false)]
        public string Name { get; set; }
    
        [DataMember(Name = "url", EmitDefaultValue = false)]
        public string Url { get; set; }
    
        [DataMember(Name = "path", EmitDefaultValue = false)]
        public string Path { get; set; }
    
        [DataMember(Name = "review", EmitDefaultValue = false)]
        public Review Review { get; set; }
      }
    }
    

    or using [JsonProperty] in Newtonsoft.Json

    using Newtonsoft.Json;
    using System;
    
    namespace Reviews.Models
    {
        public class Review
        {
    
        [JsonProperty("id")]
        public int Id { get; set; }
    
        [JsonProperty("name")]
        public string Name { get; set; }
    
        [JsonProperty("city")]
        public string City { get; set; }
    
        [JsonProperty("date")]
        public DateTime Date { get; set; }
    
        [JsonProperty("destination")]
        public string Destination { get; set; }
    
        [JsonProperty("header")]
        public string Header { get; set; }
    
        [JsonProperty("hotel")]
        public string Hotel { get; set; }
    
        [JsonProperty("hotelRating")]
        public int HotelRating { get; set; }
    
        [JsonProperty("rating")]
        public int Rating { get; set; }
    
        [JsonProperty("showComment")]
        public bool ShowComment { get; set; }
    
        [JsonProperty("text")]
        public string Text { get; set; }
    
        [JsonProperty("photo")]
        public string Photo { get; set; }
    }
    
    public class ReviewSearchResult
    {
        [JsonProperty("id")]
        public int Id { get; set; }
    
        [JsonProperty("name")]
        public string Name { get; set; }
    
        [JsonProperty("url")]
        public string Url { get; set; }
    
        [JsonProperty("path")]
        public string Path { get; set; }
    
        [JsonProperty("review")]
        public Review Review { get; set; }
        }
    }
    

    Thanks,

    Bjarne

  • Bilal Haidar 144 posts 410 karma points
    Sep 20, 2016 @ 06:29
    Bilal Haidar
    0

    Hi Shannon,

    Where in the source code I can check what global formatters are applied to all controllers?

    Thanks

  • Shannon Deminick 1525 posts 5271 karma points MVP 2x
    Sep 20, 2016 @ 07:35
    Shannon Deminick
    0

    There really aren't any of relevance: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Web/WebBootManager.cs#L235

    We don't apply global filters that will affect your own controllers, Umbraco doesn't get in your way. We apply the filters we need in our own controllers at our controller level.

  • Cristhian Amaya 52 posts 423 karma points
    Nov 06, 2016 @ 13:45
    Cristhian Amaya
    3

    Here's the code in case anyone is wondering how to create the attribute to return the data in camel case

    public class AngularJsonMediaFormatterCamelCase : AngularJsonMediaTypeFormatter
    {
        public AngularJsonMediaFormatterCamelCase()
        {
            SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        }
    }
    
    public class AngularJsonOnlyConfigurationCamelCaseAttribute : AngularJsonOnlyConfigurationAttribute
    {
    
        public override void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
        {
            var toRemove = controllerSettings.Formatters.Where(t => (t is JsonMediaTypeFormatter) || (t is XmlMediaTypeFormatter)).ToList();
            foreach (var r in toRemove)
            {
                controllerSettings.Formatters.Remove(r);
            }
            controllerSettings.Formatters.Add(new AngularJsonMediaFormatterCamelCase());
        }
    
    }
    

    Cheers.

  • Lars-Erik Aabech 349 posts 1100 karma points MVP 7x c-trib
    Dec 20, 2016 @ 22:27
    Lars-Erik Aabech
    0

    I just realized I had found the same, yet more high-level solution as this and posted it below. Didn't understand it until I'd re-read the thread, so deleted again. :P Anyways, interesting details to read here for anyone wondering how it looks under the hood.

    https://blogs.msdn.microsoft.com/jmstall/2012/05/11/per-controller-configuration-in-webapi/

  • Shannon Deminick 1525 posts 5271 karma points MVP 2x
    Dec 22, 2016 @ 03:20
    Shannon Deminick
    1

    I blogged about this a while back too :)

    http://shazwazza.com/post/webapi-per-controller-configuration/

Please Sign in or register to post replies

Write your reply to:

Draft