GithubHelp home page GithubHelp logo

chsakell / multi-client-api Goto Github PK

View Code? Open in Web Editor NEW
19.0 4.0 5.0 266 KB

Building multi-client APIs in ASP.NET

Home Page: http://wp.me/p3mRWu-15c

License: MIT License

C# 84.15% HTML 6.63% JavaScript 8.57% CSS 0.66%

multi-client-api's Introduction

Building multi-client APIs in ASP.NET

multi-client-api-06
The project introduces a method that processes JTokens in order to return only the properties requested by the client.
  • Usage
Assuming the result of the resource uri /api/tracks/1 is the following:
{
  "TrackId": 1,
  "AlbumId": 1,
  "Bytes": 11170334,
  "Composer": "Angus Young, Malcolm Young, Brian Johnson",
  "GenreId": 1,
  "MediaTypeId": 1,
  "Milliseconds": 343719,
  "Name": "For Those About To Rock (We Salute You)",
  "UnitPrice": 0.99
}

You can request only specific properties of that resource by making the request /api/tracks/1?props=bytes,milliseconds,name

{
  "Bytes": 11170334,
  "Milliseconds": 343719,
  "Name": "For Those About To Rock (We Salute You)"
}

The algorithm supports nested navigation properties as well. If /api/albums/1 returns..

{
  "AlbumId": 1,
  "ArtistName": "AC/DC",
  "Title": "For Those About To Rock We Salute You",
  "Track": [
    {
      "TrackId": 1,
      "AlbumId": 1,
      "Bytes": 11170334,
      "Composer": "Angus Young, Malcolm Young, Brian Johnson",
      "GenreId": 1,
      "MediaTypeId": 1,
      "Milliseconds": 343719,
      "Name": "For Those About To Rock (We Salute You)",
      "UnitPrice": 0.99
    },
    {
      "TrackId": 6,
      "AlbumId": 1,
      "Bytes": 6713451,
      "Composer": "Angus Young, Malcolm Young, Brian Johnson",
      "GenreId": 1,
      "MediaTypeId": 1,
      "Milliseconds": 205662,
      "Name": "Put The Finger On You",
      "UnitPrice": 0.99
    }
  ]
}

Then /api/albums/1?props=artistname,title,track(composer;name) should return the following:

{
  "ArtistName": "AC/DC",
  "Title": "For Those About To Rock We Salute You",
  "Track": [
    {
      "Composer": "Angus Young, Malcolm Young, Brian Johnson",
      "Name": "For Those About To Rock (We Salute You)"
    },
    {
      "Composer": "Angus Young, Malcolm Young, Brian Johnson",
      "Name": "Put The Finger On You"
    }
  ]
}

Properties in navigations should be semicolon (;) separated inside parethensis.

  • Example in API Controller
var _tracks = _trackRepository.GetAll(includeProperties).Skip(page).Take(pageSize);

var _tracksVM = Mapper.Map<IEnumerable<Track>, IEnumerable<TrackViewModel>>(_tracks);

string _serializedTracks = JsonConvert.SerializeObject(_tracksVM, Formatting.None,
    new JsonSerializerSettings()
    {
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    });

JToken _jtoken = JToken.Parse(_serializedTracks);
if (!string.IsNullOrEmpty(props))
    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());

return Ok(_jtoken);

The project is built in Visual Studio 2015 and ASP.NET Core but the technique and the method can be easily integrated in any version of ASP.NET API. In case you want to run the ShapingAPI application:

  1. Download the source code and open the solution in Visual Studio 2015
  2. Restore Nuget and Bower packages
  3. Install the Chinook database in your SQL Server by running the script inside the SQL folder.
  4. Alter the appsettings.json file to reflect your database environment.
  5. Run the application

Donations

For being part of open source projects and documenting my work here and on chsakell's blog I really do not charge anything. I try to avoid any type of ads also.

If you think that any information you obtained here is worth of some money and are willing to pay for it, feel free to send any amount through paypal.

Paypal
Buy me a beer

Follow chsakell's Blog

Facebook Twitter
Microsoft Web Application Development
facebook twitter-small

License

Code released under the MIT license.

multi-client-api's People

Contributors

chsakell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

multi-client-api's Issues

Skip and Take

On all your controllers you have Skip(page).Take(pageSize);
Should it not be
.Skip((page -1) * pageSize).Take(pageSize);

Unrequired _properties on all Controllers

On all your controllers you have
private List _properties = new List();
which you also initiate again further down the page.
This property does not appear to be used anywhere?

Remove Duplicate Code

Create a TokenService.

public class TokenService
{
    public static JToken CreateJToken(object obj, string props)
    {
        string _serializedTracks = JsonConvert.SerializeObject(obj, Formatting.None,
            new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });

        JToken jtoken = JToken.Parse(_serializedTracks);
        if (!string.IsNullOrEmpty(props))
            Utils.FilterProperties( jtoken, props.ToLower().Split(',').ToList());

        return jtoken;
    }
}

Then change your contollers

            var _tracks = _trackRepository.GetAll(_includeProperties).Skip((page - 1) * pageSize).Take(pageSize);

            var _tracksVM = Mapper.Map<IEnumerable<Track>, IEnumerable<TrackViewModel>>(_tracks);

            JToken _jtoken = Services.TokenService.CreateJToken(_tracksVM, props);
            return Ok(_jtoken);

AND
var _trackVM = Mapper.Map<Track, TrackViewModel>(_track);

            JToken _jtoken = Services.TokenService.CreateJToken(_trackVM, props);
            return Ok(_jtoken);

Unboxing Variable in Utils.cs

I've perused the Utils class and made modification to reduce the number of unboxing calls.

public class Utils
{
    public static void FilterProperties(JToken token, List<string> fields)
    {
        try
        {
            JContainer container = token as JContainer;
            if (container == null) return;

            JProperty jprop = null;
            JObject jobj = null;

            JProperty pNested = null;
            JObject poNested = null;

            List<JToken> removeList = new List<JToken>();
            foreach (JToken el in container.Children())
            {
                if (null != ( jprop = el as JProperty))
                {
                    if (fields.Any(f => f.StartsWith(el.Path.ToLower() + "(")))
                    {
                        string nestedProperty = fields.First(f => f.StartsWith(el.Path.ToLower() + "("));
                        int startField = nestedProperty.IndexOf("(");
                        int lastField = nestedProperty.LastIndexOf(")");
                        string nestedFields = nestedProperty.Substring(startField + 1, (lastField - 1) - startField);

                        List<string> _nestedFieldList = GetNestedFields(nestedFields);

                        JToken nestedProperties = el.First();
                        List<JToken> removeListNested = new List<JToken>();
                        JContainer nestedPropertiesContainer = nestedProperties as JContainer;
                        foreach (JToken elNested in nestedPropertiesContainer.Children())
                        {
                            if (null != (pNested = elNested as JProperty))
                            {
                                if (!_nestedFieldList.Contains(pNested.Path.ToLower().Substring(pNested.Path.IndexOf('.') + 1)))
                                    removeListNested.Add(pNested);
                            }
                            else if (null != (poNested = elNested as JObject))
                            {
                                JContainer poNestedContainer = poNested as JContainer;
                                foreach (JToken _poNested in poNestedContainer.Children().OrderBy(order => order.Parent))
                                {
                                    if (!(_poNested.ToString().Contains('(') && _poNested.ToString().Contains(';') && _poNested.ToString().Contains(')'))
                                        && !_poNested.ToString().Replace(" ", "").Replace("\r\n", "").Contains("[{"))
                                    {
                                        if (!_nestedFieldList.Contains(_poNested.Path.ToLower().Substring(_poNested.Path.IndexOf('.') + 1)))
                                            removeListNested.Add(_poNested);
                                    }
                                    else
                                    {
                                        JArray jar;
                                        JContainer _poNestedContainer = _poNested as JContainer;
                                        foreach (JToken el2 in _poNestedContainer.Children())
                                        {
                                            if ( null != (jprop = el2 as JProperty))
                                            {
                                                if (fields.Any(f => f.StartsWith(el2.Path.ToLower() + "(")))
                                                {
                                                    string nestedProperty2 = fields.First(f => f.StartsWith(el2.Path.ToLower() + "("));
                                                    int startField2 = nestedProperty.IndexOf("(");
                                                    int lastField2 = nestedProperty.LastIndexOf(")");
                                                    string nestedFields2 = nestedProperty.Substring(startField + 1, (lastField - 1) - startField);
                                                }
                                            }
                                            else if (null != (jar = el2 as JArray))
                                            {
                                                if (_nestedFieldList.Contains(el2.Path.ToLower().Substring(el2.Path.ToLower().LastIndexOf('.') + 1)))
                                                    break;

                                                JObject job;
                                                JContainer jarContainer = jar as JContainer;
                                                foreach (JToken el3 in jarContainer.Children())
                                                {
                                                    if (null != ( job = el3 as JObject))
                                                    {
                                                        string _nestedField = _nestedFieldList.FirstOrDefault(f => f.ToLower().StartsWith(_poNested.Path.ToLower().Substring(_poNested.Path.IndexOf('.') + 1) + "("));

                                                        if (string.IsNullOrEmpty(_nestedField))
                                                        {
                                                            removeListNested.Add(_poNested);
                                                            break;
                                                        }

                                                        int startField2 = _nestedField.IndexOf("(");
                                                        int lastField2 = _nestedField.LastIndexOf(")");
                                                        string nestedFields2 = _nestedField.Substring(startField2 + 1, (lastField2 - 1) - startField2);

                                                        string[] _nestedFields = nestedFields2.Split(',');
                                                        JContainer jobContainer = job as JContainer;
                                                        foreach (JToken el4 in jobContainer.Children())
                                                        {
                                                            if (!_nestedFields.Contains(el4.Path.ToLower().Substring(el4.Path.ToLower().LastIndexOf('.') + 1)))
                                                                removeListNested.Add(el4);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        foreach (JToken elNest in removeListNested)
                        {
                            elNest.Remove();
                        }

                        if (fields.Contains(nestedProperty))
                            fields.Remove(nestedProperty);

                        if (fields.Contains(el.Path.ToLower()))
                            fields.Remove(el.Path.ToLower());
                    }
                    else if (jprop != null && !fields.Contains(jprop.Name.ToLower()))
                    {
                        removeList.Add(el);
                    }
                }
                else
                {
                    jobj = el as JObject;
                    JContainer jobjContainer = jobj as JContainer;
                    foreach (JToken _joNested in jobjContainer.Children().OrderBy(order => order.Parent))
                    {
                        if (fields.Any(field => field.ToLower().StartsWith(_joNested.Path.ToLower().Substring(_joNested.Path.IndexOf('.') + 1) + "(")))
                        {
                            string nestedProperty = fields.First(f => f.StartsWith(_joNested.Path.ToLower().Substring(_joNested.Path.IndexOf('.') + 1) + "("));
                            int startField = nestedProperty.IndexOf("(");
                            int lastField = nestedProperty.LastIndexOf(")");
                            string nestedFields = nestedProperty.Substring(startField + 1, (lastField - 1) - startField);

                            List<string> _nestedFieldList = GetNestedFields(nestedFields); 
                            JToken nestedProperties = _joNested.First();
                            List<JToken> removeListNested = new List<JToken>();
                            JContainer nestedPropertiesContainer = nestedProperties as JContainer;
                            foreach (JToken elNested in nestedPropertiesContainer.Children())
                            {
                                if (null != (pNested = elNested as JProperty))
                                {
                                    if (!_nestedFieldList.Contains(pNested.Path.ToLower().Substring(pNested.Path.IndexOf('.') + 1)))
                                        removeListNested.Add(pNested);
                                }
                                else if (null != (poNested = elNested as JObject))
                                {
                                    JContainer poNestedContainer = poNested as JContainer;
                                    foreach (JToken _poNested in poNestedContainer.Children().OrderBy(order => order.Parent))
                                    {
                                        if (_nestedFieldList.Any(f => f.ToLower().StartsWith(_poNested.Path.ToLower().Substring(_poNested.Path.LastIndexOf('.') + 1) + "(")))
                                        {
                                            string field = _nestedFieldList.First(f => f.ToLower().StartsWith(_poNested.Path.ToLower().Substring(_poNested.Path.LastIndexOf('.') + 1) + "("));
                                            int startNestedField = field.IndexOf("(");
                                            int lastNestedField = field.LastIndexOf(")");
                                            string nestedFields2 = field.Substring(startNestedField + 1, (lastNestedField - 1) - startNestedField);

                                            List<string> _nestedFieldList2 = nestedFields2.Split(',').ToList();
                                            JToken nestedProperties2 = _poNested.First();
                                            List<JToken> removeListNested2 = new List<JToken>();
                                            JContainer nestedProperties2Container = nestedProperties2 as JContainer;
                                            foreach (JToken elNested2 in nestedProperties2Container.Children())
                                            {
                                                if (null != (pNested = elNested2 as JProperty))
                                                {
                                                    if (!_nestedFieldList2.Contains(pNested.Path.ToLower().Substring(pNested.Path.IndexOf('.') + 1)))
                                                        removeListNested.Add(pNested);
                                                }
                                                else if (null != (poNested = elNested2 as JObject))
                                                {
                                                    JContainer poNestedContainer2 = poNested as JContainer;
                                                    foreach (JToken _poNested2 in poNestedContainer2.Children().OrderBy(order => order.Parent))
                                                    {
                                                        if ((!_nestedFieldList2.Contains(_poNested2.Path.ToLower().Substring(_poNested2.Path.LastIndexOf('.') + 1))))
                                                            removeListNested.Add(_poNested2);
                                                    }
                                                }
                                            }

                                        }

                                        else if ((!_nestedFieldList.Contains(_poNested.Path.ToLower().Substring(_poNested.Path.LastIndexOf('.') + 1))))
                                            removeListNested.Add(_poNested);
                                    }
                                }
                            }
                            foreach (JToken elNest in removeListNested)
                            {
                                elNest.Remove();
                            }

                            if (fields.Contains(nestedProperty) && el.Next == null)
                                fields.Remove(nestedProperty);

                            if (fields.Contains(el.Path.ToLower()))
                                fields.Remove(el.Path.ToLower());
                        }
                        else if (!fields.Contains(_joNested.Path.ToLower().Substring(_joNested.Path.IndexOf('.') + 1)))
                            removeList.Add(_joNested);
                    }
                }
            }

            foreach (JToken el in removeList)
            {
                el.Remove();
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    private static List<string> GetNestedFields(string fields)
    {
        if (!fields.Contains('(') && !fields.Contains(')'))
            return fields.Split(';').ToList();

        List<string> _fieldList = new List<string>();
        string _tempField = string.Empty;
        char[] _fieldArray = fields.ToCharArray();
        bool _skip = false;

        try
        {
            for (int i = 0; i < _fieldArray.Length; i++)
            {
                if (_fieldArray[i] != ';' && _fieldArray[i] != ')')
                {
                    _tempField += _fieldArray[i];

                    if (_fieldArray[i] == '(')
                    {
                        _skip = true;
                    }
                }
                else if (_fieldArray[i] == ';')
                {
                    if (!_skip)
                    {
                        _fieldList.Add(_tempField);
                        _tempField = string.Empty;
                    }
                    else
                    {
                        _tempField += ',';
                    }
                }
                else if (_fieldArray[i] == ')')
                {
                    _tempField += ')';
                    _fieldList.Add(_tempField);
                    _tempField = string.Empty;
                    _skip = false;
                }

            }
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return _fieldList;
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.