Primary Objectives:

  1. Be able to write a MVC web application that employs AJAX to build responsive views
  2. Learn how to use an existing API to acquire data, serverside
  3. Demonstrate the use of JSON and AJAX
  4. Demonstrate custom routing URLs

Overall Requirements:

  • This application should have only one page (main view)
  • All functionality is created with Javascript and AJAX calls to action methods that don't return views (ok, you'll have to have one view for the main page itself)
  • Use JSON as the data exchange format
  • All Javascript must be placed into a separate file (in the Scripts folder) and included via a @section section
  • All CSS rules must be placed into a separate file (in the Content folder) and included in the shared layout
  • Use a different controller for AJAX calls, that fits with your custom routing (below)
  • Write custom routing rule(s) for retrieving data via AJAX with appropriate REST-like URLs
  • Use jQuery
  • Use CSS to make it look nice
  • Use a database for logging of requests

"To make sure I'm keeping up with the times I want to build a web page that uses Javascript and asynchronous calls to the web server to create a responsive design solution for a simple problem."

This homework is all about creating a responsive, single-page application. An extreme case of this kind of application is the GMail client. We don't have those kinds of aspirations, but we can definitely get the main process down.

So the page that first loads (from the server) doesn't have any data. It presents the interface only. But it does load your Javascript, which requests data from your server via asynchronous web requests and then builds the page content as desired. Users then interact with the page and more asynchronous calls are placed to send data to and receive data back from your server. The benefit is that, perhaps surprisingly, the time to load HTML, CSS and associated resources, and build and render a DOM, is actually significant. Not doing those things, while using Javascript to retrieve data and modify the DOM, often creates a more responsive page for the user. We want to learn about this as a viable option for building parts of your project next year.

Here's some good documentation from the Mozilla Developer Network.

A second purpose of this homework is to learn how to use an existing API as a resource. Ideally we would use a real REST API like those offered by Lyft (Docs), Twitter (Docs) or Trello (Docs). It is certainly possible to use these. But, examples like these require you to apply for a developer api key or authentication credentials and be really responsible in how you use it. Yes, of course we'll be responsible, but it would be better to choose something a bit less risky. The one we'll use isn't a comprehensive REST API, but that's OK. It's safe, kind of silly, easy and you can have your api key in seconds.

We will use an API from Giphy, the world's precious repository of cat and internet meme gifs.

Questions/Tasks:

  1. Start by creating a simple, single-page web application that allows users to search Giphy for gifs. Get rid of the nav bar. For now, use a single text input and a button for search.

    Wireframe of initial design
    Figure 1: The initial simple design.
  2. Next up, go to Giphy and register as a developer. Create an application and they'll give you your API key. This key is a secret. Don't put it in your repository. (I'll show you in class how to hide this secret from your repo and still use it in your code.)

    Read their documentation so you understand how to use their API. We'll start with their Search API. Try out some searches with your key. I find that using Firefox Developers Edition is really nice for this because it parses the JSON and displays it in tree form with syntax highlighting. I can also highly recommend Postman as a way to easily send customized GET and POST requests (i.e. set header fields) and analyze the responses.

    Get to know the correct format and terms for a search as well as the JSON data that is returned.

    For example, here is an object that is returned by searching for “lobsters”:

    {
    "type": "gif",
    "id": "EUHMAueGvjBsc",
    "slug": "illustration-lobster-jeremy-sengly-EUHMAueGvjBsc",
    "url": "https://giphy.com/gifs/illustration-lobster-jeremy-sengly-EUHMAueGvjBsc",
    "bitly_gif_url": "https://gph.is/16RNDY2",
    "bitly_url": "https://gph.is/16RNDY2",
    "embed_url": "https://giphy.com/embed/EUHMAueGvjBsc",
    "username": "",
    "source": "https://jeremysengly.tumblr.com/post/63657884240/bibs-on-bibs-on-bibs",
    "rating": "g",
    "content_url": "",
    "source_tld": "jeremysengly.tumblr.com",
    "source_post_url": "https://jeremysengly.tumblr.com/post/63657884240/bibs-on-bibs-on-bibs",
    "is_indexable": 0,
    "import_datetime": "2013-10-10 17:58:36",
    "trending_datetime": "1970-01-01 00:00:00",
    "images": {
    	"fixed_height_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200_s.gif",
    	"width": "200",
    	"height": "200"
    	},
    	"original_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy_s.gif",
    	"width": "500",
    	"height": "500"
    	},
    	"fixed_width": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w.gif",
    	"width": "200",
    	"height": "200",
    	"size": "223339",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w.mp4",
    	"mp4_size": "82739",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w.webp",
    	"webp_size": "227028"
    	},
    	"fixed_height_small_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/100_s.gif",
    	"width": "100",
    	"height": "100"
    	},
    	"fixed_height_downsampled": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200_d.gif",
    	"width": "200",
    	"height": "200",
    	"size": "65267",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/200_d.webp",
    	"webp_size": "52044"
    	},
    	"preview": {
    	"width": "256",
    	"height": "256",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-preview.mp4",
    	"mp4_size": "36667"
    	},
    	"fixed_height_small": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/100.gif",
    	"width": "100",
    	"height": "100",
    	"size": "83413",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/100.mp4",
    	"mp4_size": "31414",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/100.webp",
    	"webp_size": "92126"
    	},
    	"downsized_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-downsized_s.gif",
    	"width": "500",
    	"height": "500",
    	"size": "50600"
    	},
    	"downsized": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-downsized.gif",
    	"width": "500",
    	"height": "500",
    	"size": "619459"
    	},
    	"downsized_large": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.gif",
    	"width": "500",
    	"height": "500",
    	"size": "619459"
    	},
    	"fixed_width_small_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/100w_s.gif",
    	"width": "100",
    	"height": "100"
    	},
    	"preview_webp": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-preview.webp",
    	"width": "138",
    	"height": "138",
    	"size": "47982"
    	},
    	"fixed_width_still": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w_s.gif",
    	"width": "200",
    	"height": "200"
    	},
    	"fixed_width_small": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/100w.gif",
    	"width": "100",
    	"height": "100",
    	"size": "83413",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/100w.mp4",
    	"mp4_size": "31414",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/100w.webp",
    	"webp_size": "92126"
    	},
    	"downsized_small": {
    	"width": "500",
    	"height": "500",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-downsized-small.mp4",
    	"mp4_size": "153987"
    	},
    	"fixed_width_downsampled": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w_d.gif",
    	"width": "200",
    	"height": "200",
    	"size": "65267",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/200w_d.webp",
    	"webp_size": "52044"
    	},
    	"downsized_medium": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.gif",
    	"width": "500",
    	"height": "500",
    	"size": "619459"
    	},
    	"original": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.gif",
    	"width": "500",
    	"height": "500",
    	"size": "619459",
    	"frames": "30",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.mp4",
    	"mp4_size": "223632",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.webp",
    	"webp_size": "841142"
    	},
    	"fixed_height": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/200.gif",
    	"width": "200",
    	"height": "200",
    	"size": "223339",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/200.mp4",
    	"mp4_size": "82739",
    	"webp": "https://media3.giphy.com/media/EUHMAueGvjBsc/200.webp",
    	"webp_size": "227028"
    	},
    	"looping": {
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-loop.mp4",
    	"mp4_size": "846719"
    	},
    	"original_mp4": {
    	"width": "480",
    	"height": "480",
    	"mp4": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy.mp4",
    	"mp4_size": "223632"
    	},
    	"preview_gif": {
    	"url": "https://media3.giphy.com/media/EUHMAueGvjBsc/giphy-preview.gif",
    	"width": "119",
    	"height": "119",
    	"size": "49163"
    	},
    	"480w_still": {
    	"url": "https://media2.giphy.com/media/EUHMAueGvjBsc/480w_s.jpg",
    	"width": "480",
    	"height": "480"
    	}
    },
    "title": "illustration jeremy sengly GIF"
    }

    You get an array of objects, each of which represents a gif. Inside is id, title, rating, source, and URL's for many versions of the image. Remember, this is what you get at your server. You'll use C# to parse this, extract what's needed and build a JsonResult to send to your client.

  3. Now here's the plan.
    1. The user enters a search word or phrase and clicks the search button.
    2. You have previously registered a Javascript callback function on the button in order to run custom Javascript code.
    3. Your code makes an AJAX call to a controller action method, passing it the search term(s).
    4. Your action method assembles a GET request (using your API key) and sends it off to Giphy's servers. You receive a JSON response to your server containing matches to the search.
    5. Using this, extract and assemble whatever data is needed in order for your client Javascript to construct the resulting page for your user that displays the matching Gifs in a grid. You send this data from the action method to the client as a JsonResult object, which is then processed by your Javascript code to modify the DOM and display the images.
    6. Other than the initial page load of an empty search box, all communication between the client and your server must be via JSON.
    7. If the user then types in another search query and hits return, you do all this over again and the page never reloads -- it's dynamic.
    8. For the AJAX call, use jQuery's ajax method.
    9. Your controller action method for handling the AJAX request needs to use a custom route (in AppStart/RouteConfig.cs ) with an API feel. i.e. ~/search/giphy?q=cute+cat
    Wireframe of initial design
    Figure 2: After clicking the search button, or hitting enter, the user sees matching images displayed below.
  4. Now add a little static content and CSS magic to the page to make it look nicer. How about a header or header image? CSS to style the search results?
  5. Add additional features to the search. Add radio buttons, check boxes or buttons, etc. to provide the user with some options for their search. Search by rating? (Hmmm, if you do this maybe don't put the R-rated search results in your Portfolio.) Search results only return static gifs, or to prefer animated gifs, ... something interesting or useful. Whatever it is it needs to involve both client and serverside processing.
  6. Add a database to your project to keep a log of search requests. Make a table that will hold information about requests made to your server. When a request comes in, add an entry to the table with information about the request. You should log things like date/time, what the request was, the IP address of the requestor and the client's browser agent type.
  7. And as usual, write it all up in your Portfolio. This one definitely needs screenshots of your application in use. Also show your logging results.