By Daniel Du
In part 1 of this serials, I learned how to draw a basic triangular face in a web page with WebGL, and the coordinates of the triangular face are hardcoded. I would like to get them from a cloud service. I am planning to use Windows Azure to host this service and use SQL Azure to store my surface data. In this post, I will describe how I implement the web service.
With Visual Studio 2010 + Windows Azure SDK, I can create a Windows Azure Cloud Service easily. I selected a ASP.NET MVC 4 as web role. Windows Azure SDK helps me to setup the local emulation environment, so that I can develop and test my cloud service locally, even without knowing it actually is a cloud service. But debugging such a cloud project on a local cloud emulation environment is pretty slow, and it is not necessary for me at this stage, so I just removed the cloud project, and just work on the ASP.net MVC project. After all, I can add the cloud project and publish it to Windows Azure latter. So currently, I am just working on a ASP.NET MVC 4 web application.
If you have read the [Creating a cloud based viewer] serials of my colleague Philippe Leefsma, you will know that it is possible to create a RESTful web service with WCF. but I would like to try the new ASP.NET Web API in my project. I seems that Web API is much easier for me than WCF in this project, but Web API is not a replacement of WCF for everywhere, this article describe the difference and which one you should pick.
Firstly I will create my models. It is pretty straight forward, code snippet goes as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SurfaceStoreServiceWebRole.Models
{
public class TinSurface
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<TriangleFace> TriangleFaces
{ get; set; }
}
public class TriangleFace
{
public int Id { get; set; }
public virtual Point P1 { get; set; }
public virtual Point P2 { get; set; }
public virtual Point P3 { get; set; }
public string Color { get; set; }
}
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
}
Then add a repository interface and a class to implement this interface. The repository is used to handle the data from various data sources, it can be a database or a simple array in memory for testing, and the service only ask data from the repository, in this way, we can change the backing store without rewriting the service class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SurfaceStoreServiceWebRole.Models
{
interface ITinSurfaceRepository
{
IEnumerable<TinSurface> GetAll();
TinSurface Get(int id);
TinSurface Add(TinSurface surface);
TinSurface Remove(int id);
bool Update(TinSurface surface);
}
}
Firstly add a simple repository in memory, just for quick test.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SurfaceStoreServiceWebRole.Models
{
//very simple implementation, no error checking, just for quick test
public class TinSurfaceMemoryRepository : ITinSurfaceRepository
{
List<TinSurface> surfacesStorage = new List<TinSurface>();
public TinSurfaceMemoryRepository()
{
//add some test data
surfacesStorage.Add(
new TinSurface
{
Id = 1,
Name = "surface1",
Description = "this is test surface 1",
TriangleFaces = new List<TriangleFace>(){
new TriangleFace{
Id = 1,
P1 = new Point{X = 10, Y = 10, Z= 0},
P2 = new Point{X = -10, Y = 0, Z= 0},
P3 = new Point{X = 15, Y = 10, Z= 0},
Color = "0xffffff"}
}
});
}
public IEnumerable<TinSurface> GetAll()
{
return surfacesStorage.AsEnumerable<TinSurface>();
}
public TinSurface Get(int id)
{
return surfacesStorage.Find(p => p.Id == id);
}
public TinSurface Add(TinSurface surface)
{
surfacesStorage.Add(surface);
return surface;
}
public TinSurface Remove(int id)
{
TinSurface surf = Get(id);
surfacesStorage.Remove(surf);
return surf;
}
public bool Update(TinSurface surface)
{
TinSurface surf = Get(surface.Id);
if (surf != null)
{
surfacesStorage.Remove(surf);
surfacesStorage.Add(surface);
return true;
}
else
{
return false;
}
}
}
}
Now it is time to add a controller, controller is an object that handles HTTP request. In Solution Explorer, right-click the the Controllers folder. Select Add and then select Controller. In the Add Controller wizard, name the controller as "TinSurfacesController". In the Template drop-down list, there are many templates to make your work easy. I select Empty API Controller here. Then click Add. Then add code as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using SurfaceStoreServiceWebRole.Models;
namespace SurfaceStoreServiceWebRole.Controllers
{
public class TinSurfacesController : ApiController
{
//Calling new ProductRepository() in the controller is not the best design, because it ties the controller to a particular implementation of IProductRepository. For a better approach, see Using the Web API Dependency Resolver.
//http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver
//static readonly ITinSurfaceRepository repository =
// new TinSurfaceRepository();
static readonly ITinSurfaceRepository repository =
new TinSurfaceMemoryRepository();
//api/TinSurfaces
public IEnumerable<TinSurface> GetAllTinSurfaces()
{
return repository.GetAll();
}
//api/TinSurfaces/id
public TinSurface GetTinSurface(int id)
{
TinSurface surface = repository.Get(id);
if (surface == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return surface;
}
//GET api/TinSurfaces?name=name
public IEnumerable<TinSurface> GetTinSurfacesByName(string name)
{
return repository.GetAll().Where(
p => string.Equals(p.Name, name,
StringComparison.OrdinalIgnoreCase));
}
//create,POST api/TinSurfaces
public HttpResponseMessage PostTinSurface(TinSurface surface)
{
surface = repository.Add(surface);
var response = Request.CreateResponse<TinSurface>(
HttpStatusCode.Created, surface);
string uri = Url.Link("DefaultApi",
new TinSurface{Id = surface.Id});
response.Headers.Location = new Uri(uri);
return response;
}
//update, PUT api/TinSurfaces/id
public void PutTinSurface(int id,TinSurface surface)
{
surface.Id = id;
if (!repository.Update(surface))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
//delete, DELETE api/TinSurfaces/id
public void DeleteTinSurface(int id)
{
TinSurface surface = repository.Get(id);
if (surface == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(id);
}
}
}
Here is the table to of example URLs:
Action | HTTP Method | URL |
Get all surfaces | GET | api/TinSurfaces |
Get one surface by Id | GET | api/TinSurfaces/1 |
Get one surface by name | GET | api/TinSurfaces?name=surface1 |
Create a new surface | POST | api/TinSurfaces |
Update a surface with new object | PUT | api/TinSurfaces/1 |
Delete a surface by Id | DELETE | api/TinSurfaces/1 |
Now it is time to run and test this service. I use Fiddler to test the REST service. Switch Composer tab ,input the url http://localhost:3297/api/tinsurfaces and select “GET” method, then click “Execute” button:
The result HTTP code is 200, that means all work fine, double click the item to check the output in Inspector tab, I saw the result in JSON format.
It works pretty good! Okay, I would like to stop here and will continue work on this project in next post.
Comments