Click or drag to resize
AlchemyWeb Services

Plugins may contain Web Services that allow your client code (or other extension points) to communicate with. While traditional Anguilla utilized WCF for the development of web services that can be consumed by your extensions, Alchemy4Tridion takes advantage of WebAPI controllers for their ease of use, both in creation and consuming. A4T also generates a JavaScript proxy for you that allows you to consume your controller's actions without having to add any additional AJAX libraries to your code.

This topic contains the following sections:

Creating a WebAPI Controller

  1. In your plugin project, add a new class that inherits from Alchemy4Tridion.PluginsAlchemyApiController. You can name your class whatever you want.

    Note Note

    If you are using the Alchemy4Tridion Developer Pack, you can use the "Alchemy Api Controller" item template found in the "A4T" category when in the "Add New Item" dialog.

  2. You can add an optional service name to your controller by adding a AlchemyRoutePrefixAttribute to your controller's class. A service name is used in the generation of the url routes to your controller as well as in the automatically created JavaScript proxy. A service name is typically used to dinstinguish between two different controllers.

    Tip Tip

    Remember, the AlchemyRoutePrefixAttribute is completely optional.

    C#
    using Alchemy4Tridion.Plugins;
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    
    [AlchemyRoutePrefix("MyService")]
    public class MyController : AlchemyApiController
    {
    
    }

    Assuming that the above controller was a part of a plugin named MyPlugin, url's to your actions would begin with /Alchemy/Plugins/MyPlugin/api/MyService, and the generated JavaScript proxies would be attached to the automatically generated Alchemy.Plugins.MyPlugin.Api.MyService object.

    If the above controller did not contain the attribute, the url's to the actions would begin with /Alchemy/Plugins/MyPlugin/api and the attached proxy functions would go to the Alchemy.Plugins.MyPlugin.Api object.

  3. Next you'll add methods that represent the actions that can be consumed. This works the same as standard WebApi development, only your actions should always have a Verb Attribute and a RouteAttribute attached to it (the proxy makes use of these and there's a custom route table generator that requires it).

    C#
    [HttpGet]
    [Route("hello")]
    public string Hello()
    {
        return "Hello World";
    }

    Assuming the controller's class was decorated with the [AlchemyRoutePrefix("MyService")] attribute, a route would be created that accepts GET requests with a url of /Alchemy/Plugins/MyPlugin/api/MyService/hello, which would return a response that had the body of "Hello World". If the automatically generated JavaScript proxy was used, it would be utilized via Alchemy.Plugins.MyPlugin.Api.MyService.hello();.

    If the controller's class did not have the AlchemyRoutePrefixAttribute, the url to this action would be /Alchemy/Plugins/MyPlugin/api/hello and the proxy would be utilized via Alchemy.Plugins.MyPlugin.Api.hello();.

Route Variables

You can also have dynamic routes that accepts variables that will be constructed from the given url.

Important note Important

Although Web API controllers support both route parameters and query string parameters, the WebApi JavaScript proxy currently does not support query string params so avoid using them if you wish to call your controllers with it. As an alternative, you can create a binding model and a submit a POST for any additional variables you need to pass to your controller.

Adding Route Parameters

  1. Add a RouteAttribute to your action methods that you wish to have a parameters for and define your parameters in the url template string.

    C#
    [HttpGet]
    [Route("hello/{helloName}")]
    public string Hello()
    {
    
    }
  2. Add arguments to your method that match the name in your given url template string.

    C#
    [HttpGet]
    [Route("hello/{helloName}")]
    public string Hello(string helloName)
    {
        return "Hello " + helloName;
    }
    Tip Tip

    You can have multiple route parameters in your route. The order that they are defined in the url template does not have to match the order that they appear as arguments in the method definition. Only the names matter.

  3. You can access your controller's action using the generated JavaScript proxy.

    JavaScript
    Alchemy.Plugins.MyPlugin.Api.hello("AlchemyDeveloper")
      .success(function (greeting) {
        alert(greeting);
      });
Binding Models for POST Requests

If you need to submit complex data such as a values from a submitted form, a complex JSON object, or just a lot of possible configuration values, it would be better to submit a POST request using a model that the values can bind to.

Creating a Binding Model and POSTing

  1. Create a C# class (POCO) that represents the data that will be submitted to your action.

    C#
    public class PersonModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public string Bio { get; set; }
    }
  2. Add a HttpPostAttribute and a RouteAttribute to your action method. Add the model type as the argument to the action method.

    C#
    [HttpPost]
    [Route("hello")]
    public string Hello(PersonModel person)
    {
        SaveBioData(person);
    
        return String.Format("Hello {0} {1}! Your data has been saved!", person.FirstName, person.LastName);
    }
  3. Call your new action with the generated JavaScript proxy.

    JavaScript
    Alchemy.Plugins.MyPlugin.Api.hello({
      firstName: "Alex",
      lastName: "Klock",
      age: 100,
      bio: "A4T Lover"
    }).success(function (greeting) {
      alert(greeting);
    });
  4. If needed, you can combine route parameters with a binding model. When doing so, the binding model must be the last argument supplied in the action method (the proxied JavaScript expects it to be the last one).

    C#
    [HttpPost]
    [Route("greetings/{greetingType}/{fromName}/hello")]
    public string Hello(string fromName, string greetingType, PersonModel person)
    {
    SaveBioData(person);
    
    return String.Format("{0} {1} {2}! From {3}!",
        greetingType,
        person.FirstName,
        person.LastName,
        fromName);
    }

    Calling your action from the JavaScript proxy would add the model object at the end as well.

    JavaScript
    Alchemy.Plugins.MyPlugin.Api.hello("Hello", "Content Bloom", {
    firstName: "Alex",
    lastName: "Klock",
    age: 100,
    bio: "A4T Lover"
    }).success(function (greeting) {
    alert(greeting);
    });
Adding the JavaScript WebApi Proxy to a Resource Group

In order to use the JavaScript Proxy in your other script files within your resource group, you'll have to call the AddWebApiProxy method from within the resource group's constructor. See the Resource Group Section for more information.

C#
public MyResourceGroup()
{
AddFile("MyScript.js");
AddWebApiProxy();
}
Using the WebApi JavaScript Proxy

Once the JavaScript proxy has been added, files within your resource group may begin to use the automatically generated script. The script offers a couple of different flavors of dealing with ajax calls to your web service. The full namespace of the proxy is built using a combination of your plugin's name and whether or not your controller contains a service name (see above).

Error First Callback Style

The first style of dealing with communicating with your webservices is to use the error first callback style. This means that you will pass in a callback function as the last argument of your function call, which will contain two parameters. The first is an error object, which if it returns null or undefined means there was no error, otherwise it'll contain the error itself. The second parameter is the response object (which will be set if there's no error).

JavaScript
var service = Alchemy.Plugins.MyPlugin.Api;

service.doSomethingCool(function (error, response) {
    if (error) {
        console.error(error);
    } else {
        alert("We have success! " + response);
    }
});

As mentioned, the callback should always go last, after any route parameters and post data.

JavaScript
service.postSomethingCool("Article", { header: "Header", isNew: true }, function (error, response) {
    if (error) {

    } else {

    }
});

Promise Pattern

If no callback function is passed to your proxy method, then the method will return a promise. Alchemy4Tridion promises contain three methods that you can stack: success, error, and complete. The success will be called on successfull response status code, the errors will be called if there is an error status code, and the complete will be called no matter what. If you have both a success and a complete, they will both be called. A good way of thinking of them is as a try/catch/finally block.

JavaScript
service.doSomethingCool()
    .success(function (response) {
        alert("I was successful!");
    })
    .error(function (errorCode, errorObject) {
        alert("I was an error!");
    })
    .complete(function () {
        alert("I get called at the end no matter what!");
    });
Getting Your Plugin's Instance

It may be desirable to get your plugin's IAlchemyPlugin instance (for example, to get your plugin's custom IPluginSettings via the Settings property. Your AlchemyApiController has a Plugin property that will return the initialized plugin object used in the Plugin Library.

Using Tridion Core Services
Important note Important

The feature discussed in this section was added in version 0.6.1.

One of the most common things that you'll actually need to create an AlchemyApiController web service for is to communicate with Tridion via the CoreService API. Working with Core Services in an AlchemyApiController is cake with the Client property. This property lazilly initiates a new AlchemySessionAwareCoreServiceClient instance , which is a wrapper of the SessionAwareCoreServiceClient that safely implements IDisposable. Not only is calling the Dispose method completely safe (no worries about exceptions that would leave your resources open), but the AlchemyApiController will also automatically call the Client property's Dispose method at the end of the request. If the developer has manually called Dispose there is no issue as it'll also check to see if its already been disposed.

Note Note

The Client property is lazily initizated, which means the value is actually null until you use this property.

Note Note

The Client property is automatically impersonated as the logged in Tridion user making the request.

Caution note Caution

The automatic disposing of the AlchemySessionAwareCoreServiceClient instance only works with what's assigned to the Client property. If you create your own instances as local variables or as separate members of your controller, you'll have to dispose of those yourself.

AlchemySessionAwareCoreServiceClient Sample
[HttpGet]
[Route("schemas/{publicationId}")]
public IEnumerable<string> GetAllSchemas(string publicationId)
{
    RepositoryItemsFilterData filterData = new RepositoryItemsFilterData();
    filterData.ItemTypes = new[]
      {
            ItemType.Schema
      };
    filterData.Recursive = true;

    // just an example, in real life we wouldn't use GetList for this as GetListXml would be faster
    return Client.GetList(publicationId, filterData) 
        .Select(item => item.Id)
        .ToList();
    // no need to do Client.Dispose() as it'll automatically be called at the end of the request
}
Getting the Tridion User's Information

Another common thing to do in web services is to get information about the user making the request. The AlchemyApiController contains the User property for just this reason, which is an instance of IUserService. You can use this property to get the name of the currently logged in Tridion User, to get the get the UserData instance of the user, as well as to check if the user is a system administrator or not.

C#
[HttpGet]
[Route("admins/doStuff")]
public bool DoAdminOnly()
{
    if (!User.IsSystemAdministrator())
    {
        throw new Exception("Uh Oh...");
    }
    // ... 
}
See Also