Building a photoblog with Nancy and Simple.Data Part 2: Defining the routes

This is the second part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

In the previous post we created the Visual Studio solution, added the necessary references to Nancy and Simple.Data and tested two basic routes to see if Nancy was setup correctly. In this post we will decide what routes we will need in the application and we will configure our NancyModules accordingly. Let’s get started!

What routes do we need?

To answer that question, we first need to determine what kind of information our application needs to expose and how people should interact with it. Once we know that, we can map the requirements to a route and a method (POST or GET). To make things a bit easier, we will split up the requirements in two parts: front end and back end. The front end of the photoblog is visible for everyone while the back end is only visible for the logged in administrator, me.

Front end requirements

  • Visit the homepage to view the latest photo
      Route: /
      Method: GET

  • Visit a specific photo with its comments
      Route: /photo/{slug}
      Method: GET

  • Add a comment to a specific photo
      Route: /photo/{slug}/addcomment
      Method: POST

  • Visit the general archives
      Route: /archives
      Method: GET

  • Visit the archives for a certain year
      Route: /archives/{year}
      Method: GET

  • Visit the archives for a certain month of a certain year
      Route: /archives/{year}/{month}
      Method: GET

Back end requirement

  • View the login form
      Route: /admin
      Method: GET

  • Login on the system
      Route: /admin/login
      Method: POST

  • View the list of photo’s
      Route: /admin/photos
      Method: GET

  • Add a new photo
      Route: /admin/photos/add
      Method: GET and POST

  • Edit a photo
      Route: /admin/photos/edit/{slug}
      Method: GET and POST

  • View a list of comments
      Route: /admin/comments
      Method: GET

  • Delete a comment
      Route: /admin/comments/delete/{id}
      Method: POST

Now we have that figured out, we can start organizing the routes into modules.

Creating the modules

I think it’s probably easier to split the routes into multiple modules. I think we will need five:

  1. One for the homepage
  2. One for the photo’s
  3. One for the archives
  4. One for the admin stuff
  5. A base class to provide common functionality to the other modules

The base class

For now, the base class doesn’t need to do anything special, but as soon as we start working with the database it will come in handy. Let’s create the base class first so all other modules are future proof.

Create a new directory in the root of the project and call it Modules. Add a new class called PhotoblogModule to that new directory. Make the class abstract, let it inherit from NancyModule and add the following code to the class:

public abstract class PhotoblogModule : NancyModule
{
	public PhotoblogModule() : base()
	{

	}

	public PhotoblogModule(string modulePath) : base(modulePath)
	{

	}
}

This gives us two constructors. We’ve already used the default parameterless constructor in the first post. The second constructor however is new. It accepts one parameter called modulePath and we’ll see what that does in just a moment.

The homepage module

Add another class to the Modules directory and call it RootModule. Let it inherit from the PhotoblogModule class we just made and the one route it should support:

public class RootModule : PhotoblogModule
{
	public RootModule() : base()
	{
		Get["/"] = parameters =>
		{
			return "Homepage";
		};
	}
}

The photo module

Just like before, add a new class to the Modules directory, call it PhotoModule, let it inherit from PhotoblogModule as well and add the two routes it needs to take care of:

using Nancy;

public class PhotoModule : PhotoblogModule
{
	public PhotoModule() : base("/photo")
	{
		Get["/{slug}"] = parameters =>
		{
			return String.Format("Photo '{0}'", parameters.slug);
		};

		Post["/{slug}/addcomment"] = parameters =>
		{
			string photoSlug = Convert.ToString(parameters.slug);
			return Response.AsRedirect("/photo/" + photoSlug);
		};
	}
}

Did you see how we are calling base("/photo")? That’s the second constructor of our PhotoblogModule class. The modulePath parameter makes sure that all routes specified in the Get[] and Post[] indexers are relative to the modulePath. So when we add Get["/{slug}"], we are actually adding Get["/photo/{slug}"].

The archives module

Again, add a new class to the Modules directory, call it ArchivesModule, let it inherit from PhotoblogModule as well and add the routes it needs to handle:

public class ArchivesModule : PhotoblogModule
{
	public ArchivesModule() : base("/archives")
	{
		Get[""] = parameters =>
		{
			return "All photo's of all years and months.";
		};

		Get["/{year}"] = parameters =>
		{
			return String.Format("All photo's of the year {0}",
				parameters.year);
		};

		Get["/{year}/{month}"] = parameters =>
		{
			return String.Format("All photo's of month {0} of the year {1}",
				parameters.month,
				parameters.year);
		};
	}
}

See how we used the second constructor again to specify a modulePath?

The admin module

The last one! Add another class to the Modules directory, call it AdminModule, let it inherit from PhotoblogModule as well and add these routes:

public class AdminModule : PhotoblogModule
{
	public AdminModule() : base("/admin")
	{
		Get[""] = parameters =>
		{
			return "Display the login form.";
		};

		Post["/login"] = parameters =>
		{
			// Perform validation, then redirect
			return Response.AsRedirect("/admin/photos");
		};

		Get["/photos"] = parameters =>
		{
			return "A list of all the photo's.";
		};

		Get["/photos/add"] = parameters =>
		{
			return "Display the form to add a photo.";
		};

		Post["/photos/add"] = parameters =>
		{
			// Add the photo, then redirect
			string slug = "newPhoto";
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/photos/edit/{slug}"] = parameters =>
		{
			return String.Format("Display the form to edit a photo called '{0}'.",
				parameters.slug);
		};

		Post["/photos/edit/{slug}"] = parameters =>
		{
			// Edit the photo, then redirect
			string slug = Convert.ToString(parameters.slug);
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/comments"] = parameters =>
		{
			return "A list of all the comments.";
		};

		Post["/comments/delete/{id}"] = parameters =>
		{
			// Delete the comment, then redirect
			return Response.AsRedirect("/admin/comments");
		};
	}
}

There. That was the last of our modules.

Wrapping things up

The project structureNow that we have our five modules in the Modules directory, we can delete the MainModule.cs we created in the first post since we won’t be needing it anymore. All modules and routing information are nicely grouped in the Modules directory. Our project now looks like the one on the right.

The changes to source code can be found on Github.

In the next post we’ll try to replace those temporarily returned strings with Razor views.

This entry was posted in Development, MyPhotoBlog and tagged . Bookmark the permalink.

9 Responses to Building a photoblog with Nancy and Simple.Data Part 2: Defining the routes

  1. Pingback: Building a photoblog with NancyFX and Simple.Data Part 1: Setting up the project | Kristof Claes

  2. Hi!

    I just wanted to point out that the modulePath for the ArchivesModule sample is wrong, it still says ‘/photo’

    It might also be useful for you to know that you can enter a regex for your route, it could be useful for you since you want to capture dates, so you could limit it to only capture digits for {year} etc. Here is an old gist that shows how to use it (normal regex capture groups) https://gist.github.com/834598

  3. Kristof says:

    Yeah, I was in a bit of hurry when copy-pasting the code for the ArchiveModule apparently. It should be alright now :-)

    Thanks for the regex tip! That will definitely come in handy!

  4. Just one comment – you probably want to break out the login route(s) (the one that shows the login page, and the one that takes the login) because, if you use the built in security, then it’s secured a the module level and you’ll end up with a secured login page :-)

    There’s a forms auth sample in the main project if you want to take a looksie.

  5. Kristof says:

    A secured login page would be rather useless indeed :-) Thanks for the tip! I’m making this up as I go along. I’ll bump into a few more problems I guess, but it’s a good way to learn things.

  6. Pingback: Building a photoblog with Nancy and Simple.Data Part 1: Setting up the project | Kristof Claes

  7. Pingback: Building a photoblog with Nancy and Simple.Data Part 3: Rendering some views | Kristof Claes

  8. Pingback: Building a photoblog with Nancy and Simple.Data Part 4: Adding the database | Kristof Claes

  9. Pingback: Building a photoblog with Nancy and Simple.Data Part 5: Updating Simple.Data | Kristof Claes

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please leave these two fields as-is: