Tech Blog

Creating a Student Portal with the New Alma APIs

Introduction

We’re going to use the new Alma REST APIs to build a basic student portal with a few features to help get you started with Alma APIs.

First, we create a basic ASP.NET MVC 4 application in C#. MVC is a popular programming style which stands for Model, View, Controller. Of course, Alma APIs support any programming language.  And the great thing about the REST style of APIs is that it’s easy to call them from any programming environment which supports HTTP calls, which is to say all of them. For more information on ASP.NET MVC applications, see here.

We modify the template, do a few cosmetic changes to make it “library like.” It’s a classic Bootstrap-based site with a banner and three sections. So we add menu items for the following entries:

  • Library card- we’ll display some basic student information and allow users to update a few key data elements.
  • Requests- we’ll show a list of the student’s requests, and allow the user to cancel them.
  • Rooms- we probably won’t get to this in the demo, but these applications always have three sections, so we needed to have a third!

As an aside, one nice feature of these Bootstrap templates is that they utilize responsive design. So they’ll automatically resize themselves in a mobile application.

Authentication

We want to simulate authentication against a campus directory. Any service provider will do- the important thing is to have access to a student’s identifier that we’ll use later when we make calls to Alma.

In our case, we can use Google’s OAuth service. Our MVC application has some scaffolding to get us started. We need to add some addtional code to access the student’s email, which we know is configured as an identifier in our Alma instance.

So in the AccountController, we add the following code to the ExternalLoginCallback:

var emailClaim = id.Claims.FirstOrDefault(x =>
        x.Type.Equals(
        "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        StringComparison.OrdinalIgnoreCase));

Session["email"] = emailClaim != null ? emailClaim.Value : null;

Students can then click the login link on our portal, and click the “Google” button under “Use another service to login.” This will set off an OAuth chain which requires the user to login with Google, give permission for our application to access their email address, and then redirects back to us. We know we’ve been successful when see our email address in the banner.

Calling Alma APIs

Since I read the Getting Started guide to Alma APIs, I know I need a few things before I can start making API calls. I logged into the Developer Network with my credentials that are hooked up to my institution. There I created a new Application, and called it Student Portal. I added access to the User APIs (which are all I’ll need for this demo), and pointed the User APIs to my sandbox instance (I don’t live dangerously enough to connect it to production yet….).

So now I have the following important details which I’ll use later:

  • API Key (some long string)
  • Base URL for the Ex Libris Developer Network APIs: https://api-na.hosted.exlibrisgroup.com/almaws/v1

Library Card – Our First API Call!

I decided to start with the Library Card. As we said above, here we’ll show some patron information and allow them to update a few of the fields. The wisdom of allowing users to change their names notwithstanding, it will be a good way to get our feet wet calling GET and PUT APIs.

Show User Information

We add a controller called, what else, CardController. In the Index method, which is what will be called when we go to /Card on our portal, we need to get the user’s information. So we populate a dynamic object based on the JSON response that we (hopefully) will get back from Alma. One of the great things about JSON is how easy it is to work with. No need here to create a local model or stub class.

public ActionResult Index()
{
       JObject user = GetUser();
       ViewBag.Message = Request.QueryString["message"];
       return View(user);
}

Our private GetUser() method is pretty simple too. It looks like this:

private JObject GetUser()
{
       var json = Utils.AlmaApiGet("/users/" + Session["email"]);
       JObject user = JsonConvert.DeserializeObject<JObject>(json);
       return user;
}

We are going to be calling a bunch of Alma APIs, so let’s create a small static Utility class which contains some helper methods, including the one to make GET calls against Alma.

We’ll create two static variables with our important information from above:

static string _BASEURL = "https://api-na.hosted.exlibrisgroup.com/almaws/v1";
static string _APIKEY = "<<MY KEY>>";

And our method to call Alma:

public static string AlmaApiGet(string uri)
{
       uri = GetAlmaUri(uri);
       var http = (HttpWebRequest)WebRequest.Create(new Uri(uri));
       http.Accept = "application/json";
       var response = http.GetResponse();
       Stream resp = response.GetResponseStream();
       using (StreamReader sr = new StreamReader(resp)) {
                return sr.ReadToEnd();
       }
}

You’ll notice a helper function which builds our Alma URL. This just encapsulates some code so we don’t repeat ourselves (DRY).

private static string GetAlmaUri (string uri)
{
    // Add a random number to ensure no caching
    Random random = new Random();
    int randomNumber = random.Next(0, 10000000);
    return String.Format("{0}{1}{2}apikey={3}&format=json&nonce={4}", _BASEURL, uri, uri.Contains("?") ? "&" : "?",
        _APIKEY, randomNumber);
}

So in our GET method, we create a new HTTP request, feed it the Alma API URL, note that we prefer JSON as our response format (thank you very much!) and read the response. Very simple.

Once we’re back in the Controller, we feed the dynamically created User object to the View. If we want to see how the object looks, we can run the URL in our favorite REST client. I like the Advanced REST Client Chrome plugin.

Library Card View

Now let’s take the information we got back from Alma and display it to the user. We create a simple Index view for the Card Controller. It uses the Razor syntax to include data from the Model we passed in. I’ll spare you all of the code since it’s mostly just an HTML form, but it works like this:

<h3>Welcome @Model["first_name"] @Model["last_name"]!</h3>

The @Model syntax gives us access to the data we got back from Alma.

Updating User Information

MVC has a feature which helps to bind form values to a model. We create a CardViewModels.cs class file, and add the following simple UserViewModel class:

public class UserViewModel
{
    public string first_name { get; set; }
    public string last_name { get; set; }
}

Of course, this class would need to be expanded to be really useful. Note the property names- we’re using the same names as the form variables to facilitate the auto binding features.

To update the user object, we create another method on the Card Controller that accepts POST requests. We indicate the routing by using the [HttpPost] attribute. Note the method takes a UserViewModel as a parameter, which will activate the auto binding.

[HttpPost]
public ActionResult Index(UserViewModel formUser)
{
    JObject user = GetUser();
    user["first_name"] = formUser.first_name;
    user["last_name"] = formUser.last_name;

    JObject updateduser = UpdateUser(user);
    ViewBag.Message = "Your details were updated successfully.";
    return View(updateduser);
}

We call the GetUser() method to get the user object from Alma. We set the relevant values on the dynamic user object with the values we got back from the form. Then we call a helper method called UpdateUser.

private JObject UpdateUser(JObject user)
{
    var json = Utils.AlmaApiPut("/users/" + Session["email"], user.ToString());
    JObject newuser = JsonConvert.DeserializeObject<JObject>(json);
    return user;
}

The UpdateUser calls another utility method which PUTs data to Alma, so we need to pass in the updated user object as a string.

public static string AlmaApiPut(string uri, string data)
{
    uri = GetAlmaUri(uri);
    var http = (HttpWebRequest)WebRequest.Create(new Uri(uri));
    http.ContentType = "application/json";
    http.Method = "PUT";

    Stream body = http.GetRequestStream();
    ASCIIEncoding encoding = new ASCIIEncoding();
    Byte[] bytes = encoding.GetBytes(data);
    body.Write(bytes, 0, bytes.Length);
    body.Close();

    var response = http.GetResponse();
    Stream resp = response.GetResponseStream();
    string _response;
    using (StreamReader sr = new StreamReader(resp))
    {
        _response = sr.ReadToEnd();
    }

    return _response;
}

We get back the updated object, pass it into the View (along with a success message- always give feedback!), and the view displays the updated user information. Simple!

 

Requests

So far, we’ve successfully called an Alma GET API and displayed information from it. We also called PUT to update the object.

Now, we want to get a list of something (requests in our case), loop through the JSON array, and display them.

Show Requests

We create a new controller, appropriately named RequestsController, and mark it with the [Authorize] attribute. We add some methods which will be familiar to us from our Users example.

public ActionResult Index()
{
    JObject requests = GetUserRequests();
    ViewBag.Message = Request.QueryString["message"];
    return View(requests);
}

private JObject GetUserRequests()
{
    var json = Utils.AlmaApiGet("/users/" + Session["email"] + "/requests");
    JObject requests = JsonConvert.DeserializeObject<JObject>(json);
    return requests;
}

Nothing surprising or unusual there. Alma APIs, like all good REST-based APIs, use a standard hierarchical structure for the URLs. So while we could always reference the docs, it’s nice to be able to guess that to get a user’s requests, we call /users/<USERNAME>/requests.

Now it’s in the View that we get a bit more funky.

Again we see the Razor syntax, and we notice that on our dynamically created Requests object, we have an array of Request objects. We can loop through them, and display information for each. We’ll add a condition to only show HOLD requests, and add a link to cancel the request (which will use momentarily).

@if (Model["total_record_count"] > 0)
{
    foreach (var request in Model["user_request"])
    {
        if (request["request_type"]=="HOLD")
        {
          <tr>
              <td>@request["title"]</td>
              <td>@request["author"]</td>
              <td>@request["pickup_location"]</td>
              <td>@Html.ActionLink("Cancel request", "Cancel",
                  new { requestId = @request["request_id"] })</td>
          </tr>
          }
      }
    }
    else
      {
        <tr><td colspan="3">No requests found.</td></tr>
      }

Here’s our list of requests:

Cancel Request

Now that we have a lovely page showing our request, we want to give the user the opportunity to say “Thanks, but no thanks.” We created a Cancel link in the View above. Now we need a server side method to handle the cancellation.

We add another method to the Requests controller, GET /Cancel. The Cancel User Request API supports the DELETE HTTP method. So our code turns out to be the smallest method yet:

public ActionResult Cancel (string requestId)
{
    Utils.AlmaApiDelete("/users/" + Session["email"] + "/requests/" + requestId);
    return RedirectToAction("", new { message = "Your request was successfully cancelled." });
}

Which calls the AlmaApiDelete Utility method, also quite compact:

public static void AlmaApiDelete(string uri)
{
    uri = GetAlmaUri(uri);
    var http = (HttpWebRequest)WebRequest.Create(new Uri(uri));
    http.Method = "DELETE";
    var response = http.GetResponse();
}

Here we really see the beauty of REST based APIs in their simplicity. We don’t even need to check the response code. Alma DELETE APIs return 203 No Content when they’re successful, so we rely on the framework to raise an error if we don’t get back a valid response code.

Wrap Up

So let’s review what we saw:

  • Created an application to get an API Key.
  • Integrated with an authentication system to get an Alma identifier.
  • Made GET calls and received JSON formatted data, including arrays.
  • Made PUT and DELETE calls to Alma APIs to update and delete objects.

Of course, this is only the beginning, and there’s a lot more we could do. We’ll add the standard disclaimer that to be production ready, we need to add better error handling and be ready for rainy day scenarios.

But I hope this gives you a jump start, perhaps makes your initial contact with Alma APIs a bit easier, and maybe even gives you some ideas for great things you could do with Alma.

Note: This entire application is available on GitHub at this repository.

Leave a Reply