In this recipe, we will see how to create an ASP.NET web API that supports CRUD operations, which stands for Create Read Update and Delete . These map to the standard database operations that correspond to the following HTTP verbs:
GET
: The GET
method retrieves whatever information is identified by the requesting URIPUT
: The PUT
method requests that the enclosed entity is to be stored under the supplied requesting URIPOST
: The POST
method requests that the enclosed entity is to be a subordinate of the resource identified by the requesting URIDELETE
: The DELETE
method requests that the resource identified by the requesting URI should be deletedIn this recipe, we will see how to implement these CRUD features on the previously created service, with the HTTP verbs, and the ASP.NET web API.
In order to use this recipe, you should have Visual Studio 2012 and ASP.NET MVC 4 installed (the latter one includes the ASP.NET web API). You should also have the project resulting from our previous recipe.
Here we are going to implement the CRUD verbs into the web API that we created in our previous section:
WebAPICRUD
.IRepository.cs
with the following code:public interface IRepository<T> where T : class { IEnumerable<T> GetAll(); T GetById(int id); void Insert(T entity); void Update(T entity); void Delete(T entity); }
BooksRepository.cs
containing the following code:public class BooksRepository : IRepository<BookModel> { ObservableCollection<BookModel> ocBooks = null; public BooksRepository() { ocBooks = GenerateBooks(); } private ObservableCollection<BookModel> GenerateBooks() { ObservableCollection<BookModel> Books = new ObservableCollection<BookModel>() { new BookModel(){ Id=1, Title = ".NET 4.5 First Look", Description = "A book to quickly and practically get into .NET 4.5" }, new BookModel(){ Id=2, Title = "The lost book of Agatha Christie", Description = "A book everybody wants to read..." } }; return Books; } public IEnumerable<BookModel> GetAll() { return ocBooks; } public BookModel GetById(int id) { var book = (from b in ocBooks where b.Id == id select b).FirstOrDefault(); return book; } public void Insert(BookModel book) { book.Id = GetLatestIdPlusOne(); ocBooks.Add(book); } private int GetLatestIdPlusOne() { int BiggestId = 0; foreach (BookModel book in ocBooks) { if (BiggestId < book.Id) { BiggestId = book.Id; } } return (BiggestId + 1); } public void Update(BookModel book) { this.Delete(book); ocBooks.Add(book); } public void Delete(BookModel book) { BookModel bookToRemove = GetById(book.Id); ocBooks.Remove(bookToRemove); } }
BooksController.cs
content:public class BooksController : ApiController { static readonly IRepository<BookModel> BooksRep = new BooksRepository(); public IEnumerable<BookModel> Get() { return BooksRep.GetAll(); } public HttpResponseMessage Get(int id) { BookModel bm = BooksRep.GetById(id); if (bm != null) { return Request.CreateResponse<BookModel>(HttpStatusCode.OK, bm); } else { return Request.CreateResponse<BookModel>(HttpStatusCode.NotFound, null); } } public void Put(BookModel bm) { BooksRep.Update(bm); HttpResponseMessage hrm = Request.CreateResponse<BookModel>(HttpStatusCode.Created, bm); } public HttpResponseMessage Post(BookModel bm) { BooksRep.Insert(bm); HttpResponseMessage hrm = Request.CreateResponse<BookModel>(HttpStatusCode.Created, bm); hrm.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = bm.Id })); return hrm; } public HttpResponseMessage Delete(BookModel bm) { BooksRep.Delete(bm); return new HttpResponseMessage(HttpStatusCode.NoContent); } }
We started from a copy of our past web API project so we could re-use the basics.
Next we implemented the repository pattern, with the IRepository
interface and the BooksRepository
class. This is a good practice to increase readability and testability, even if our actual implementation is working upon an observable collection in memory. If we wanted to improve this example to use a database, for example, we would just need to slightly change the BooksRepository
implementation of the IRepository
interface.
On this repository, we implemented an initialization of our database, the GetAll()
and Get(int id)
functions and Insert(BookModel book)
with a helper function GetLatestIdPlusOne()
to get the largest book ID and add one to it.
We also implemented the Update()
and Delete()
methods.
To follow, we built the BooksController
class, which we started from scratch. First, we added a static member of the type IRepository<BookModel>
, associating it with a new BooksRepository
:
static readonly IRepository<BookModel> BooksRep = new BooksRepository();
With this singleton in place, we can use it wherever we want from our BooksController
class, making it global to all the instances of the BooksController
class.
We implemented the GET
verbs first, in a very similar way to what we had done previously. However, in the current implementation, they are now more decoupled and the responsibility of getting the element now resides with the repository.
The PUT
, POST
, and DELETE
verbs were also very straightforward to implement, by calling the method directly.
Additionally, you will observe that we have been returning an adequate HttpResponseMessage
with each of these verbs, perfectly matching what the protocol is expecting.