Create a web service for an app for Office using the ASP.NET Web API

You may have heard that apps for Office use client-side programming when communicating with the Office object-model. You may also have heard that apps for Office are especially well-suited for web mash-up scenarios, and are ideal for calling into web services and interacting with Web content. As a developer, how do you author your own web service? This post will lead you step-by-step through creating your own Web API service and calling into it from client-side app for Office code.

Sample code: All relevant code will be shown here. To download a copy of the sample code, see Apps for Office: Create a web service using the ASP.NET Web API.

Getting started

First, when would you need to author your own web service for an app for Office?

To do this

Consider this…

Establish a database connection, or connect to a service with a username and password.

Encoding those in JavaScript is a serious security risk, as JavaScript code is trivially easy to inspect. Such calls should always be done on the server

Perform any functionality where the algorithm/logic should be kept hidden from users

Just as above, if your app encodes or transforms data (and if the algorithm for the transformation is something you need to keep secret), keeping the logic behind the server might be your only option.

Retrieve external data that is not available in JSON-P format

Browsers implement a same-origin policy that prevents scripts loaded from one domain to manipulate content in another domain. Data in JSON-P format can pass through, but other formats may not. Creating your own web service allows it to act as a “wrapper” around the external data, bypassing the browser same-origin policy limitations.

Use existing server-side code or libraries (or to share a common back-end across multiple platforms)

Suppose your app uses existing .NET Framework logic or libraries, or that you need to share a common back-end across multiple platforms. Exposing that functionality via a web service allows you to keep more of your existing code and libraries as is, without re-writing it to JavaScript.

Perform licensing validation

For apps posted to the Office Store, license checks must be performed via server-side code.

Just to be clear, not all web-service scenarios require that you author your own web service. In particular, if your app consumes data from a public service that returns data in JSON-P format, you may not need to write your own web service at all. For purposes of this article, however, we’ll assume that there is some inherent business logic or data transformation that you need to expose to your app by means of writing your own web service. Let’s get started.

Using Web API

There are several possible choices for server-side technologies. For purposes of this article, I will be demonstrating the use of the ASP.NET Web API. Web API is an excellent framework for building REST applications, and Visual Studio provides first-class tooling support for it. The ability of Web API to communicate across a broad range of platforms makes it a particularly compelling choice for communicating between an app for Office and a corresponding web service.

Scenario

Let’s take a real-life example: You have an app, and now you want to add a “Send Feedback” page. To send the feedback, you’ll need to either log it to a database or maybe just email it to your development team – but in either case, you have sensitive data (connection string or a username/password combo) that you certainly wouldn’t want to expose via JavaScript. As we saw in the “Getting started” section, this is a canonical example of masking the back-end functionality behind a web service.

To make this example match real-world scenarios as closely as possible, let’s send multiple bits of information to the service: for example, an overall rating (an integer on a 1-5 scale) along with a string for the actual feedback. Likewise, the response we’ll get from the service will include both a Boolean status flag to indicate success, and a message back from the server (e.g., “Thank you for your feedback” or an error description). By framing the problem this way, you can see how this sample is analogous to more complex scenarios, like fetching data from a database via a web service (for example, sending a query with certain parameters, returning a data structure that represents the list of results, etc.)

Figure 1 shows a screenshot of the app we’re aiming to build. Again, to download the full sample project, see Apps for Office: Create a web service using the ASP.NET Web API .

WebAPI-fig01

Figure 1. Screenshot of the “Send Feedback” sample app.

Create the app project

To get the example up and running quickly, let’s just create a new task pane app for Office project, and place the “send feedback” functionality as part of the home screen. Of course, if you have an existing project where you’d like to append this functionality, you could create a SendFeedback folder, add two files called SendFeedback.html and SendFeedback.js within it, and place the functionality there, instead.

This example assumes you’re using the latest version of Office Developer Tools. Thus, when you create a new project, you should get a project structure as shown in Figure 2.

WebAPI-fig02

Figure 2. Visual Studio project structure.

Open Home.html, and replace its <div id=”content-main”> section with the following code.

 <div id="content-main">
    <div class="padding">
        <p>This sample app demonstrates how an app for Office can 
           communicate with a Web API service.</p>
        <hr />
        <label>Overall rating: </label>
        <select id="rating" class="disable-while-sending" 
            style="width: inherit">
            <option value=""></option>
            <option value="5">&#9733;&#9733;&#9733;&#9733;&#9733;</option>
            <option value="4">&#9733;&#9733;&#9733;&#9733;</option>
            <option value="3">&#9733;&#9733;&#9733;</option>
            <option value="2">&#9733;&#9733;</option>
            <option value="1">&#9733;</option>
        </select>
        <br />
        <br />
        <label>Feedback:</label>
        <br />
        <textarea id="feedback" class="disable-while-sending" 
            style="width: 100%; height: 200px"
            placeholder="What would you like to tell us?"></textarea>
        <button id="send" class="disable-while-sending">Send it!</button>
    </div>
</div>

As you can see, the HTML is very standard, containing a dropdown for the star rating (&#9733; is the Unicode symbol for a star), a textarea for the textual feedback, and a button to send the data. Note that each of those components carries a unique ID, so that it can be easily referenced it via code. I also am applying a class called “disable-while-sending” to each of those three elements – again, just as a way to reference these elements from code – to be able to provide a status indication after the user has clicked “send”. Remember that web service calls are done asynchonrously, so disabling the controls provides both a visual indication, and prevents users from clicking “Send” multiple times while the first call is still underway.

Let’s add a corresponding JavaScript file as well, with a placeholder for the sending procedure. If you’re following along with Home.js, replace the entire contents of that file with the following code.

 /// <reference path="../App.js" />

(function () {
    "use strict";
    // The initialize function must be run each time a new page is loaded
    Office.initialize = function (reason) {
        $(document).ready(function () {
            app.initialize();
            $('#send').click(sendFeedback);
        });
    };

    function sendFeedback() {
        $('.disable-while-sending').prop('disabled', true);
        // Magic happens
    }
})();

Okay, that’s as far as we can get for now. Let’s add our web service.

Adding a Web API controller

With Web API, anytime you call a method on a server, you’re doing so via a Controller. So, let’s add a Controller to handle our SendFeedback action.

You can add a controller anywhere within your web project. By convention, however, controllers go into a “Controllers” folder at the root of the project, so let’s go ahead and create it now.

After you create the folder, if you choose it and mouse over the “Add” menu, and you should see the option to add a Web API Controller class. The same option should be available under the “Add” > “New Item…” dialog, as well. Let’s add the Web API controller class now.

WebAPI-fig03

Figure 3. Adding a Web API Controller Class.

By another convention, that is somewhat more strictly enforced, Web API controllers must end with the suffix “Controller” in order to be properly recognized by the routing mechanism. This isn’t to say you can’t change it, but that does require a bit of extra legwork. So for now, let’s appease the Web API plumbing architecture, and name our controller “SendFeedbackController”.

We can leave the auto-generated code be for the present, and move onto the next step. We’ll be returning here in a moment.

Setting up the Web API routing

There is a quick side-step we need to take in order for our Web API Controller to function: we need to register it with the web application. Some flavors of web projects, like MVC, include support for Web API routing by default. The default app for Office does not include this plumbing, but it’s not hard to add.

Note that this step is best done after you’ve already added a Controller to the project; otherwise, you need to set up some references manually. This is why this post has you first add the controller, and only then register it.

To add the Web API routing registration, right-click on the root your web project, select “Add”, and you should see “Global Application Class” as one of the options. It should also be available under the “Add” > “New Item…” dialog box. The default name of “Global.asax” should suffice.

WebAPI-fig04

Figure 4. Adding a Global Application class.

Once you’ve added Global.asax, the file should automatically open, with empty method stubs filled out. Add the following two “using” statements somewhere at the top of the file.

 using System.Web.Routing; 
using System.Web.Http;

 

The only method you need to touch is the Application_Start method. Add the following code into the Application_Start body (the rest of the methods can remain empty).

 RouteTable.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

What does this means? In a nutshell, the method registers your controllers to be available under the “api” route, followed by the name of the controller and an optional ID (in case of parameterized queries). The {ID} will not be used for our final SendFeedback controller, but it doesn’t hurt. Note that you can also specify other parameters under routeTemplate, such as “{action}”, to include the name of the action as part of the URL. Refer to "Routing in ASP.NET Web API" for more information on configuring Web API routing, if you’re curious.

Filling out the Controller

The SendFeedbackController class has some auto-generated code that demonstrates some of the capabilities of Web API, including responding to various HTTP verbs (“GET” vs. “POST”, and so on), routing based on the ID parameter, and so forth. While these capacities are certainly worth exploring on your own (e.g., for an example of how to return a list of values), they are not necessary for our current “Send Feedback” scenario. For our purposes, all we want is a single method that takes in some data (overall rating and feedback text), and returns some data (a status and a message).

Data structures

Let’s write some data structures to hold the request and response objects. You can write them as inner classes within SendFeedbackController.

 public class FeedbackRequest
{
    public int? Rating { get; set; }
    public string Feedback { get; set; }
}

public class FeedbackResponse
{
    public string Status { get; set; }
    public string Message { get; set; }
}

Nothing fancy – we’re using built-in data types (strings, integers) to build up the more complex request and response classes. If you’re curious why Rating is a nullable (int?) type, this is both to demonstrate that you can do this, and to handle the case where the user does not fill out a rating in the dropdown.

Method signature

Now let’s create the actual SendFeedback method. Go ahead and delete the other method stubs (they’re only there as examples, and you can always re-generate them by creating a new Web API Controller). In their place, let’s put a single SendFeedback method, which takes in a FeedbackRequest, and returns a FeedbackResponse.

 [HttpPost()] 
public FeedbackResponse SendFeedback(FeedbackRequest request) 
{ 
 // Magic happens 
}

We will be using the HTML “Post” verb to post data to the controller. (For a discussion of POST versus GET, search the web for “when to use POST versus GET”). Note that you can either put the word “Post” as part of the method name, or you can name the method what you’d like, and annotate it with a “[HttpPost()]” attribute. It’s a matter of personal preference.

Try-catch

Let’s wrap our method body in a try-catch block, so we can gracefully handle any errors that occur as part of the method. The “catch” is shorter, so let’s fill that out first.

 try
{
// Magic moved to here
}
catch (Exception)
{
    // Could add some logging functionality here.

    return new FeedbackResponse()
    {
        Status = "Sorry, your feedback could not be sent",
        Message = "You may try emailing it directly to the support team."
    };
}

What’s happening here? Because the controller’s method is the very outer layer of the API, we want to ensure that any exceptions are caught and handled appropriately. Thus, on failing, we will still send a response via the data structure we defined, with an appropriate status text and message. Note that as part of the “catch”, we could instead throw a new HttpResponseException rather than returning a FeedbackResponse, and handle the exception on the client – it’s a matter of preference / convenience. However, performing a try-catch on the server-side code is still be useful for logging or for a potential fallback mechanism. For a more thorough discussion of Web API error handling, see “ASP.NET Web API Exception Handling” on the ASP.NET Blog.

The mail-sending functionality

Now let’s tackle the body of the “try”. First, if we’re going to send feedback via email, we’ll probably need to gather up some credentials.

 const string MailingAddressFrom = app_name@contoso.com;
const string MailingAddressTo = "dev_team@contoso.com";
const string SmtpHost = "smtp.contoso.com";
const int SmtpPort = 587;
const bool SmtpEnableSsl = true;
const string SmtpCredentialsUsername = "username";
const string SmtpCredentialsPassword = "password";

For the subject, let’s use the name of the app and the current date, so that it’s easy to distinguish individual feedback items.

 var subject = "Sample App feedback, " 
 + DateTime.Now.ToString("MMM dd, yyyy, hh:mm tt"); 

For the body, we will append the rating and the feedback. Notice how we’re using the request object to read these values, just like we would with any normal function parameters.

 var body = "Rating: " 
 + request.Rating + "\n\n" + "Feedback:\n" 
 + request.Feedback; 

Now we create the mail message and send it via the SmtpClient class (note that you’ll need to add a “using System.Net.Mail;” statement to the top of the file).

 MailMessage mail = 
 new MailMessage(MailingAddressFrom, MailingAddressTo, subject, body); 
// Send as plain text, to avoid needing to escape special characters, etc. 
mail.IsBodyHtml = false; 
var smtp = 
 new SmtpClient(SmtpHost, SmtpPort) { EnableSsl = SmtpEnableSsl, 
 Credentials = 
   new NetworkCredential(SmtpCredentialsUsername, 
 SmtpCredentialsPassword) }; 
smtp.Send(mail);

Because we’re using a try-catch block, we know that if the code has reached past the Send command, the feedback was sent successfully. So we can return a FeedbackResponse object back to the user.

 // If still here, the feedback was sent successfully.
return new FeedbackResponse()
{
    Status = "Thank you for your feedback!",
    Message = "Your feedback has been sent successfully."
};

Our controller is now done! Now we just need the client-side piece.

Back to JavaScript

Let’s return to our sendFeedback() method stub that we left in the JavaScript code in Home.js. We’re just about ready to finish the app.

First, we want to disable the “send” button and the other fields while sending is underway.

 $('.disable-while-sending').prop('disabled', true); 

Next, prepare the data to send. Note that we create a JavaScript object to hold this data, but with property names (“Feedback” and “Rating”) matching the names of the FeedbackRequest fields from our .NET Controller.

 var dataToPassToService = { 
 Feedback: $('#feedback').val(), 
 Rating: $('#rating').val() 
};

Finally, we make a jQuery AJAX call. The URL is relative to the page form which we’re calling the request, hence we go back two levels (to escape “App/Home/”) before turning to “api/SendFeedback”. The data type is “POST”, which matches what the controller expects. For the data, we stringify the above “dataToPassToService” object, and add the appropriate content type. (Tip: While it’s not necessary for POST request, you may want to specify an explicit “cache:true” or “cache:false” for GET requests, depending on whether or not those should be cached).

 $.ajax({
    url: '../../api/SendFeedback',
    type: 'POST',
    data: JSON.stringify(dataToPassToService),
    contentType: 'application/json;charset=utf-8'
}).done(function (data) {
    // placeholder
}).fail(function (status) {
    // placeholder
}).always(function () {
    // placeholder
});

Now onto the final stretch of filling out the placeholders above. If the AJAX call succeeds, we’ll display a message to the user, using the Status and Message fields that we get from the controller’s FeedbackResponse object. Let’s substitute that into the “done” placeholder above.

 app.showNotification(data.Status, data.Message); 

If the call fails (for example, you are offline), let’s add a similar notification (“fail” placeholder).

 app.showNotification('Error', 'Could not communicate with the server.'); 

Finally, regardless of whether the call succeeded or failed, let’s re-enable the controls so that the user can try again, or send another bit of feedback, or copy-paste the feedback and send it via email. Thus, for the “always” placeholder, let’s do.

 $('.disable-while-sending').prop('disabled', false); 

Our code is now officially complete.

Run it!

Let’s have a celebratory run. Press F5 to launch the project, and add some feedback text and a rating in the appropriate fields. Press “Send”. If all is well – and if you have substituted the Controller’s email account constants with appropriate credentials – you should see a notification in the UI that your feedback has been sent, and you should also receive an email with a copy of the feedback. If you’re curious about what is happening behind the covers, you can use a tool like Fiddler to analyze the serialization “magic” that’s happening behind the covers. You can also use Visual Studio to pause on breakpoints, both in the JavaScript code and in the Controller.

WebAPI-fig05

Figure 5. Fiddler trace showing data passing in and out of the web service

The example in this post is just the tip of the iceberg: I am excited to see what apps folks will build, leveraging the flexibility and responsiveness of client-side code, and the power and security of server-side code. Please leave a comment below if you have any questions or comments.

~ Michael Zlatkovsky | Program Manager, Visual Studio Tools for Office & Apps for Office