ServiceStack support for conditionally omitting fields from a REST response on a per-call basis

后端 未结 2 929
后悔当初
后悔当初 2021-02-06 13:34

At a minimum, I\'m looking for a way to conditionally exclude certain properties on the resource from being included in the r

相关标签:
2条回答
  • 2021-02-06 14:27

    Usually you can control the serialization of your DTOs by setting the DataMember attributes. With those attributes you can control if the property should have defaults or not. Meaning if you simply do not define the property of the object you want to return, it should not be serialized and therefore will not be shown in the resulting Json.

    ServiceStack internally uses the standard DataContract...Serializer, so this should be supported

    Otherwise you could also make use of dynamic objects and simply compose your object at runtime, serialize it and send it back. Here is a very very basic example:

            var seri = JsonSerializer.Create(new JsonSerializerSettings() { });
    
            using (var textWriter = new StringWriter())
            {
                var writer = new JsonTextWriter(textWriter);
    
                dynamic item = new { Id = id };
    
                seri.Serialize(writer, item);
    
                return textWriter.ToString();
            }
    
    0 讨论(0)
  • 2021-02-06 14:28

    So...in my opinion ServiceStack's best feature is that it makes sending, receiving and handling POCOs over HTTP super easy. How you set up the POCOs and what you do in between (within the 'Service') is up to you. Does SS have opinions? Yes. Do you have to agree with them? No. (But you probably should :))

    I think expanding on something like below would get you close to how you want to handle your api. Probably not the best example of ServiceStack but the ServiceStack code/requirements are barely noticeable and don't get in your way (AppHost configure not shown). You could probably do something similar in other .NET Frameworks (MVC/Web API/etc) but, in my opinion, won't look as much like straight C#/.NET code as with ServiceStack.

    Request classes

    [Route("/Profiles/{Id}")]
    public class Profiles
    {
        public int? Id { get; set; }
    }
    
    [Route("/SocialNetworks/{Id}")]
    public class SocialNetworks
    {
        public int? Id { get; set; }
    }
    

    Base Response class

    public class BaseResponse
    {
        protected virtual string hrefPath
        {
            get { return ""; }
        }
    
        public string Id { get; set; }
        public string href { get { return hrefPath + Id; } }
    }
    

    Classes from example

    public class Profile : BaseResponse
    {
        protected override string hrefPath { get { return "https://host/profiles/"; } }
    
        public string GivenName { get; set; }
        public string SurName { get; set; }
        public string Gender { get; set; }
        public string FavColor { get; set; }
    
        public List<BaseResponse> SocialNetworks { get; set; }
    }
    
    public class SocialNetwork: BaseResponse
    {
        protected override string hrefPath { get { return "https://host/socialNetworkMemberships?profileId="; }}
    
        public string SiteName { get; set; }
        public string ProfileUrl { get; set; }
    }
    

    Services

    public class ProfileService : Service
    {
        public object Get(Profiles request)
        {
            var testProfile = new Profile { Id= "123", GivenName = "Bob", SurName = "Smith", Gender = "Male", FavColor = "Red", 
                    SocialNetworks = new List<BaseResponse>
                        {
                            new SocialNetwork { Id = "abcde", SiteName = "Facebook", ProfileUrl = "http://www.facebook.com/"}
                        }
            };
    
            if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
                return ServiceHelper.BuildResponseObject<Profile>(testProfile, this.Request.QueryString);
    
            return testProfile;
        }
    }
    
    public class SocialNetworkService : Service
    {
        public object Get(SocialNetworks request)
        {
            var testSocialNetwork = new SocialNetwork
                {
                    Id = "abcde",
                    SiteName = "Facebook",
                    ProfileUrl = "http://www.facebook.com/"
                };
    
            if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
                return ServiceHelper.BuildResponseObject<SocialNetwork>(testSocialNetwork, this.Request.QueryString);
    
            return testSocialNetwork;
        }
    }
    

    Reflection Helper Class

    public static class ServiceHelper
    {
        public static object BuildResponseObject<T>(T typedObject, NameValueCollection queryString) where T: BaseResponse
        {
            var newObject = new ExpandoObject() as IDictionary<string, object>;
            newObject.Add("href", typedObject.href);
    
            if (!String.IsNullOrEmpty(queryString.Get("fields")))
            {
                foreach (var propertyName in queryString.Get("fields").Split(',').ToList())
                {
                    //could check for 'socialNetwork' and exclude if you wanted
                    newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
                }
            }
    
            if (!String.IsNullOrEmpty(queryString.Get("expand")))
            {
                foreach (var propertyName in queryString.Get("expand").Split(',').ToList())
                {
                    newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
                }
            }
    
            return newObject;
        }
    }
    
    0 讨论(0)
提交回复
热议问题