When creating new content, applications, and/or other custom data and services within Telligent Community, you can expose those services for external integration via REST in the same way that the built-in APIs are exposed externally via REST. This offers immense flexibility in
[toc]
When Should I Use REST Endpoints?
Anytime you want to enable applications outside of Telligent Community to interact with your custom-added entities, you should expose the entities via REST. This can also be used in the UI for AJAX operations using your entities.
Our example will illustrate our standard recommended REST endpoint set for entities: Create, Update, Delete, Get, and List.
Required DLL references:
- Telligent.Evolution.Components
- Telligent.Evolution.Rest
Sample Content
The first step in the process is to have custom data or service written to create REST endpoints against. Here we present a simple example class, Dog:
using System; namespace Telligent.Evolution.Samples { public class Dog { public Guid Id { get; set; } public string Name { get; set; } public string Breed { get; set; } } }
For the sake of this demo, we assume a supporting service exists to perform CRUD operations on this object. We refer to it as DogService and assume its methods take all required parameters individually, and group all optional parameters into an Dog<MethodName>Options class.
Registering REST Routes
Implementing IRestEndpoints requires a Register() method that provides you with a controller with which to register your REST routes. Routes will always start with the standard Telligent Community REST URL (<siteurl> + "/api.ashx/v2/"); we will define what the rest of the url should look like for each desired method. IRestEndpoints is also an IPlugin, so you will need to implement the basic IPlugin interface as well.
The declaration of the endpoint is actually fairly simple. The parameters are:
- int version: Used to track breaking changes to your endpoints. Part of the infrastructure of APIs is that they remain consistent so that third party applications do not need to constantly update to prevent from breaking. If you make changes to an endpoint, it is very highly recommended to instead create a new endpoint with updated version number
- string relativeUrl: This is the addition to the base REST URL to identify this method to the handler. Together with the HTTP method, this makes the unique signature for this method.
- object parameterDefaults: Specifies default settings about the routes, such as what the REST resource name is and what REST action is being taken.
- object parameterConstraints: Constraints would be used to specify things like the required format of parameters in the route url.
- HttpMethod method: Corresponds to the standard HTTP methods (GET, POST, PUT, DELETE).
- Func<IRestRequest, IRestResponse> handler: This is the method that will actually take the request and extract the information needed to provide a response. It can either be a defined method or an inline Func<> declaration.
public void Register(IRestEndpointController restRoutes) { restRoutes.Add(1, "dogs", new { resource = "dog", action = "create" }, null, HttpMethod.Post, CreateDog); restRoutes.Add(1, "dog/{id}", new { resource = "dog", action = "update" }, null, HttpMethod.Put, UpdateDog); restRoutes.Add(1, "dog/{id}", new { resource = "dog", action = "delete" }, null, HttpMethod.Delete, DeleteDog); restRoutes.Add(1, "dog/{id}", new { resource = "dog", action = "show" }, null, HttpMethod.Get, GetDog); restRoutes.Add(1, "dogs", new { resource = "dog", action = "list" }, null, HttpMethod.Get, ListDogs); }
The functions for performing each action are declared separately. First though, we must create a Response object so that we can send the data in a known format.
[XmlRoot(ElementName = "Response")] [RestAction(RestAction.Show, "achievement")] public class DogResponse : IRestResponse { private Dog _dog; public Dog Dog { get { return _dog; } set { _dog = value; } } public string Name { get { return "Dog"; } } public object Data { get { return _dog; } } public string[] Errors { get; set; } }
Each Rest method accepts an IRestRequest object and returns and IRestResponse object. The request object is expected to have all parameters contained in the PathParameters object. Depending on if a parameter is required or optional, an error response can be returned if the request does not fulfill the requirements. If all required parameters are included, each can be parsed and then used with the underlying internal service to perform the operation requested.
Create is the first time this object is known to your service, so it needs all required parameters to create the object. In this example, the Id creation is handled internally so the only required parameter is Name.
public IRestResponse CreateDog(IRestRequest request) { var response = new DogResponse();
To check for a required parameter, we just check if it exists in the PathParameters dictionary and either return early if missing, or assign to a variable if present. Additional checks could be made here if, for instance, a required parameter needed to be an integer.
if (!request.PathParameters.ContainsKey("Name")) { response.Errors = new[] { "Name is required" }; return response; } var name = request.PathParameters["Name"].ToString();
Checking for optional parameters is easier; we only need to assign it to the options object if it does exist in PathParameters.
var options = new DogCreateOptions(); if (request.PathParameters.ContainsKey("Breed")) options.Breed = request.PathParameters["Breed"].ToString();
Once all the information you need is collected, you can make a call to your internal service, perform the necessary operations, and return relevant information in the REST response. It is important to wrap these steps in a try...catch block so you can ensure you return a REST response to the request, and not an ASP.net error.
try { var createdDog = DogService.Create(name, options); response.Dog = createdDog; } catch (Exception ex) { response.Errors = new[] { "Error: '{0}'", ex.ToString() }; } return response; }
Update uses the Id originally returned from Create to find the object, which allows all other parameters to be optional and only change if specified.
public IRestResponse UpdateDog(IRestRequest request) { var response = new DogResponse(); if (!request.PathParameters.ContainsKey("Id")) { response.Errors = new[] { "Id is required" }; return response; } var id = Guid.Parse(request.PathParameters["Id"].ToString()); var options = new DogUpdateOptions(); if (request.PathParameters.ContainsKey("Name")) options.Name = request.PathParameters["Name"].ToString(); if (request.PathParameters.ContainsKey("Breed")) options.Breed = request.PathParameters["Breed"].ToString(); try { var createdDog = DogService.Update(id, options); response.Dog = createdDog; } catch (Exception ex) { response.Errors = new[] { "Error: '{0}'", ex.ToString() }; } return response; }
Delete only needs the Id of the object to remove it from the database.
public IRestResponse DeleteDog(IRestRequest request) { var response = new DefaultRestResponse(); if (!request.PathParameters.ContainsKey("Id")) { response.Errors = new[] { "Id is required" }; return response; } var id = Guid.Parse(request.PathParameters["Id"].ToString()); try { DogService.Delete(id); } catch (Exception ex) { response.Errors = new[] { "Error: '{0}'", ex.ToString() }; } return response; }
Get also needs only the Id to return the full object.
public IRestResponse GetDog(IRestRequest request) { var response = new DogResponse(); var options = new DogGetOptions(); if (!request.PathParameters.ContainsKey("Id")) { response.Errors = new[] { "Id is required" }; return response; } options.Id = Guid.Parse(request.PathParameters["Id"].ToString()); try { var dog = DogService.Get(options); response.Dog = dog; } catch (Exception ex) { response.Errors = new[] { "Error: '{0}'", ex.ToString() }; } return response; }
List is the most flexible method in terms of what can be returned from it, and therefore includes no required parameters but many additional parameters for customizing the returned list.
public IRestResponse ListDogs(IRestRequest request) { var response = new DefaultRestResponse(); var options = new DogListOptions(); if (request.PathParameters.ContainsKey("Name")) options.Name = request.PathParameters["Name"].ToString(); if (request.PathParameters.ContainsKey("Breed")) options.Breed = request.PathParameters["Breed"].ToString(); if (request.PathParameters.ContainsKey("SortBy")) options.SortBy = request.PathParameters["SortBy"].ToString(); if (request.PathParameters.ContainsKey("SortOrder")) options.SortOrder = request.PathParameters["SortOrder"].ToString(); if (request.PathParameters.ContainsKey("PageSize")) options.PageSize = Int32.Parse(request.PathParameters["PageSize"].ToString()); if (request.PathParameters.ContainsKey("PageIndex")) options.PageIndex = Int32.Parse(request.PathParameters["PageIndex"].ToString()); try { var dogs = DogService.List(options); response.Name = "Dogs"; response.Data = dogs; } catch (Exception ex) { response.Errors = new[] { "Error: '{0}'", ex.ToString() }; } return response; }
Using the Registered Routes
For information on making calls to REST end points, see REST API: Making Requests.