Written on 4/18/2014 in Web development

First phase - REST API


The vorpal story Data REST Service First phase

GET http://universe.com/hello/world

REST, isn’t it wonderful? It’s the coding unicorn of our time. Rarely, a harsh word is said about it and for a certain group of people its functionality is endless and clear. I’m not one of them and if you agree that REST api’s aren’t a perfect solution, skip the next few paragraphs. While REST api’s definitely work and the simplicity, conventions and protocol set are amazing. I still don’t find it to be the solution for everything. I’m saying this because more recently, I was working on a small application where WCF was used to perform certain actions. While our reason for using WCF was mainly for its easy to implement security, I would probably never have used a REST api there. I’m certain a lot of you disagree and that it’s possible to implement a REST api with an equivalent level of security, but it’s just not that easy.

Nevertheless, some guy came to us, asked us what we were doing and we answered his question. It was all very polite. He then proceeds to ask us why we weren’t building a RESTful api. I took a sip from my tea and replied: ‘Well, it had to be set up fast and the security is built-in.. WCF is awesome‘. His reply, I formatted it a bit to make it shorter than the history of the world that was his version: ‘API’s are awesome, you should also think they’re awesome, they’re the best at everything, …‘, I digress. My co-worker was already humming and covering his ears to numb the pain of stupidity but I was trying to listen. After a brief discussion and a small escalation, we buried him somewhere in the woods1.

Over-analysing

REST api’s are resource based. I think we can all agree with that. Sometimes, I find myself in the position where the stuff I want to call and receive aren’t resources. - Some people are offended by this point. - I don’t like using a REST api for a non-resource based action. - A lot of people are offended, mainly because they think my reasoning is incorrect. - While they might be right, I do not like them and they should shut up. I will only write a REST API when I need access to the resources itself and where non-resource based actions are an exception. These exceptions can then be covered by ‘actions’ or ‘methods’.

So, For the RESTful api, I’m using ASP.Net MVC5 Web API 2. Wow, that’s a mouthful. I had an experience with web api 1, I liked it, but it had some serious problems out of the box. While I believe these problems were solvable, I was not in a hurry to solve them. From what I read, web api 2 was great out of the box and I concur. I set up the project using the default template and individual user authentication. Anyone with some background in ASP.Net MVC will immediately find its way around the web api. It’s clear how things work and the code looks clean and neat. Both qualities I find important.

Setting up the web api

I set up authorization on all controllers using the [Authorize] attribute on the class since there are only 3. You can also use a global attribute. This will make sure the user is authenticated. There are a few actions however that I want to be public. On these I will override the [Authorize] attribute with the [AllowAnonymous] attribute.

For the moment, default functionality of the web api is good enough. I turned off the xml serializer because that one is just annoying, but aside from that I did not change much. I did however, add an action! ‘GetShortPosts’ in the Post controller, does exactly what the Get action does. However, it limits the post length to only get the first X characters (for the time being). I plan to use this in the list of posts to give a short description about every post. Why do I use an action for this, you ask?

Well, the answer is simple. I first considered another option, where I add an optional url parameter to define the length of the post that gets returned. While this solution would have been rather clean and would have worked, the semantics of getting the first X characters of the post was wrong. I don’t want readers to only see a greeting or something else that’s at the beginning of my posts. This might not capture the content of the post and that’s exactly its use. I did not provide this extra bit of information yet in my data layer but I want to provide it in the future. So I defined the action in a way so when the time comes, I only need to change the content of the return value. The underlying process for the website should not change.

In the controllers, I’ve put some minor auditing. In the update actions, the entity’s ‘ChangedDate’ will be set to today’s date. In the remove actions, the entity’s ‘Inactive’ boolean will be set to true. In the add actions, the ‘AddedDate’ will be set to today’s date. Here is the shortened post controller. The Put action, for now, is unused because editing is not implemented yet.

/// <summary>
/// Post api controller
/// </summary>
[Authorize]
public class PostController : ApiController
{
    private readonly IUnitOfWork _uow;
    private readonly IPostRepository _postRepo;
    private readonly ITagRepository _tagRepo;
    private readonly ITopicRepository _topicRepo;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="uow">Injected by ninject: Unit of Work</param>
    /// <param name="postRepo">Injected by ninject: Post repository</param>
    /// <param name="tagRepo">Injected by ninject: Tag repository</param>
    /// <param name="topicRepo">Injected by ninject: Topic repository</param>
    public PostController(IUnitOfWork uow, IPostRepository postRepo, ITagRepository tagRepo, ITopicRepository topicRepo)
    {
        _uow = uow;
        _postRepo = postRepo;
        _tagRepo = tagRepo;
        _topicRepo = topicRepo;
    }

    /// <summary>
    /// Get a specific post: GET api/topic/5
    /// </summary>
    /// <param name="id">The post id</param>
    /// <param name="topic">The post's topic</param>
    /// <returns>Post</returns>
    [AllowAnonymous]
    public IHttpActionResult Get(int id, string topic)
    {
        var postDto = Mapper.Map<PostDto>(_postRepo.GetPostBySequenceNumber(id, topic));
        if (postDto == null)
        {
            return NotFound();
        }

        var prev = _postRepo.GetAll().OrderByDescending(m => m.SequenceNumber).FirstOrDefault(m => m.SequenceNumber < postDto.Id);
        var next = _postRepo.GetAll().OrderBy(m => m.SequenceNumber).FirstOrDefault(m => m.SequenceNumber > postDto.Id);

        postDto.PreviousPost = prev == null ? null : (int?)prev.SequenceNumber;
        postDto.NextPost = next == null ? null : (int?)next.SequenceNumber;
        postDto.PreviousPostTitle = prev == null ? null : prev.Title;
        postDto.NextPostTitle = next == null ? null : next.Title;

        return Ok(postDto);
    }

    /// <summary>
    /// Update a post: PUT api/topic/5
    /// </summary>
    /// <param name="id">The post id to update</param>
    /// <param name="value">The post values to update</param>
    public IHttpActionResult Put(int id, [FromBody]AddOrUpdatePostDto value)
    {
        var basePost = _postRepo.GetById(id);
        if (basePost == null)
        {
            return NotFound();
        }
        basePost = Mapper.Map(value, basePost);
        basePost.ChangedDate = DateTime.Now;
        basePost.Topic = _topicRepo.GetTopicBySequenceNumber(value.TopicId);
        basePost.Tags = _tagRepo.GetMany(m => value.TagIds.Contains(m.SequenceNumber)).ToList();

        var changedPost = _postRepo.Update(basePost);
        _uow.SaveChanges();

        return Ok(Mapper.Map<PostDto>(changedPost));
    }
}

There’s nothing difficult going on here. In the Put action I just get the base post from the underlying data context, I use automapper to map the updated values from the dto2 to the updated post, do some extra changes and updates and then update the entity. The most important thing to notice here is that a IHttpActionResult is returned. This is a wrapper that allows more control to the returned object. The base controller that is automatically created when creating a web api 2 project returns a string. When you just return a string, you return either a 200 OK status code or a 500 Internal Server status code. Because we return a IHttpActionResult we can use the ‘Ok()’ and ‘NotFound()’ methods for returning a 200 status code with an object or a 404 status code. We can also manually select a status code.

AutoMapper and Ninject

I use automapper to map my entities to the dto’s. Its available through NuGet and it’s a very easy way for introducing some magic to your application. It maps one object’s fields/properties/… to another objects fields/properties/… It works great and a big plus is that you can separate the mapping logic entirely from all other code. You just create a map on initialisation, and then call ‘Mapper.Map<>()’ anywhere in the application. An example configuration for the tag mapping is below:

// Map a tag object to a tagdto object
Mapper.CreateMap<Tag, TagDto>()
    .ForMember(dest => dest.Id, action => action.MapFrom(from => from.SequenceNumber));

// Map a tagdto object to a map object
Mapper.CreateMap<TagDto, Tag>()
    .ForMember(dest => dest.ChangedDate, action => action.Ignore())
    .ForMember(dest => dest.AddedDate, action => action.Ignore())
    .ForMember(dest => dest.Posts, action => action.Ignore())
    .ForMember(dest => dest.SequenceNumber, action => action.MapFrom(from => from.Id))
    .ForMember(dest => dest.Id, action => action.Ignore());

In this example, we ignore several properties and map the sequence number from our entities as an id for our Dto. Because obviously, we don’t want to show Id’s with no semantic meaning to readers. As a matter of fact, most navigation including tags and topics is done using the tag and topic name. Only post navigation is done primarily using the sequence number for obvious purposes.

Dependency injection will be used to inject the controllers with the necessary repositories. Ninject is my dependency injector of choice, it’s easy to use and has some good features. There are also Nuget packages available to set it up. Ninject’s bootstrapper uses WebActivator to initialise itself before any mvc modules are loaded. The bootstrapper code:

kernel.Bind<TheVorpalBlog.Data.VorpalContext>().To<TheVorpalBlog.Data.VorpalContext>().InRequestScope();
kernel.Bind<Generics.Repository.IUnitOfWork>().To<Generics.Repository.UnitOfWork>();
kernel.Bind<TheVorpalBlog.Repository.IPostRepository>().To<TheVorpalBlog.Repository.PostRepository>();
kernel.Bind<TheVorpalBlog.Repository.ITagRepository>().To<TheVorpalBlog.Repository.TagRepository>();
kernel.Bind<TheVorpalBlog.Repository.ITopicRepository>().To<TheVorpalBlog.Repository.TopicRepository>();

For a simple tutorial about dependency injection, you can just visit Ninject’s ‘dojo‘, the tutorial starts with an introduction explaining why, how and when to use dependency injection. It’s one of those things, that when you used it once, everything you did before resembled you as a caveman making fire rubbing two sticks together. It worked, but it was just not easy and too time consuming. Check out dependency injection if you didn’t already.

Testing the web api was not done automated. <Horror sound playing in the background> I believe there is no business case here and I might write some automated testing in the future. But for now, I’ll stick to some small scripted tests just to make sure everything does what it’s supposed to do. I use a chrome app called Postman. It’s a REST client that I have used in the past. Unlike Fiddler - which is great as well - it does not capture traffic. It’s a simple request-response app. You can add everything you should need for testing a REST api like headers, JSON body, url parameters … Another nice feature of Postman is that you can set up collections with saved requests so you don’t have to build your request from scratch every time. These collections can also be exported / imported for use on different computers.

BTW, I don’t actually drink tea.. I don’t drink coffee either.. I drink chocolate milk, so that’s quite embarrassing as a developer… See you next week/post!

Newer Older Top
blog comments powered by Disqus