In this post I’ll show you how to build your own Linkshortener microservice. If you are still wondering what a microservice is, check out my previous post Microservices! Micro.. what?.
We will be focussed on building a simple REST API that provides the basic functionality we need to create Linkshortener application. If you need to get an idea about what a Linkshortener is, just think about tinyURL or bit.ly where you can submit a long URL and they create a short hash from it. This makes it easier to share a short link with other people instead of the original long URL. When people navigate to this short URL, they will be redirected to the original long URL. The implementation today will be a very simplified version and it will only implement the REST API behind the graphical web interface. In future posts we will be gradually extend this project to highlight other aspects like REST Security, building a frontend with AngularJS, Continuous Delivery with Docker and so on.
Before going any further lets look what operations we want to support in the API. We will be exposing a Links endpoint on URL http://localhost:8080/links and it will support two HTTP methods:
GET
: Find all links stored in our databasePOST
: Create a new hash based on the URL sent by the user
Besides that we’ll also create an additional endpoint that’s mapped on the root http://localhost:8080/{hash}
that will do the actual redirect after we make a request to the original long url. In production we ideally want a very short domain like bit.ly
, which is only 6 characters long.
You can find all source code for this blog on my GitHub repository. Feel free to send in pull requests if you see anything that can be improved.
Start building your Linkshortener
As mentioned in the previous post, a microservice needs to handle a specific feature or business requirement. In this case the feature is to shorten links that are too long in size to make them easier to share with others.
In that blog post we also took a quick look at Spring Boot to start an application based on a solid foundation. I will use this today as well, so to start I’ll use the Spring Initializr application to bootstrap the application. We’ll be using two Spring dependencies: Web and Data JPA, which will make sure we will be able to get up and running with Spring Web and Spring Data JPA out of the box. The only thing this does which is visible to us is adding two Spring Boot dependencies to the pom.xml
Let’s have a look at the technology we will be using to get started building the API. You’ll notice quite a few testing frameworks since I think it plays an important role in every application. I won’t go into detail in this post, but you’ll find unit and integration tests in the GitHub repository.
- Java 1.8
- Spring Boot, Spring JPA, Spring Web
- HSQLDB: Embedded database
- JUnit, Mockito, AssertJ, RESTAssured
Not all these dependencies can be chosen from the initializr application, so let’s add them other ones to the pom
Database layer: Spring Data JPA and HSQLDB
Let’s start adding a HSQLDB dependency, this will be our embedded database to get started quickly. As soon as you add this dependency, Spring Boot will see this when the context starts up and it will load default configuration for it so we don’t have to do anything at all to get started with it.
Next up let’s define the JPA Entity that we want to store in the database. I’ll do this by creating a undefined in the undefined package.
#1 We start by defining a JPA entity by using the @Entity
and @Table
annotations.
#2 We will use the Spring Data JPA’s auditing capabilities to save the date when an entity is created or modified, we add the AuditEntityListener
class which will let us annotate date fields as @CreatedDate
and @LastModifiedDate
and these will be filled in automatically when we persist this entity to the database.
#3 It might be interesting to see, we won’t be using the broken java.util.Date anymore since we are using JDK8 we can use the new Date and Time API. To make this work when we persist and entity we’ll need to add the @EnableJpaAuditing
to our Application class.
#4 The @Version
can be used by Hibernate to help solve concurrency issues. For each update the version number will be incremented and if we have concurrent updates the first one to finish will be persisted and the other updates will see that they are working on an old version of the entity.
What you’ll see below is the new @SpringBootApplication
annotation, which is available since Spring Boot 1.2.0.RC2, which replaces the annotations @Configuration
, @ComponentScan
and @EnableAutoConfiguration
that are usually used for Spring Boot applications.
To work with these entities we’ll add a LinkRepository that extends from the Spring Data JPA interface JpaRepository
and implement custom find methods based on the LinkEntity hash and url fields.
When you are done doing this, you are doing implementing the database layer including JPA auditing. No EntityManagers, no DataSource configuration, nothing at all. Just add our database driver in the pom.xml, implement a Spring interface and that’s it. Spring Data JPA will expose all our basic CRUD methods automatically because we have implemented the JpaRepository interface. Besides that, we wanted to more specific methods, find by hash and find by URL, this will be generated based on the method names. Since Url and Hash are properties on our LinkEntity, Spring Data JPA will know what SQL we need to implement these queries. Pretty easy right?
REST: Spring Web and business logic
Let us start by implementing the Links REST endpoint and create the POST method that we specified to create a new short link hash. This method will take an URL and store it in the database together with its hash as an LinkEntity. We don’t want to directly call our JPA repository from the REST endpoint, so let us add a LinkService interface which will be the glue between our endpoint and database that contains all the actual business logic that needs to be performed.
We’ll implement this service in a minute, but let’s create a LinkController first and put it in the be.drissamri.rest package. We will annotate the class with the Spring 4 @RestController
class that basically annotates all methods with @ResponseBody
to specify that we’ll be returning JSON Objects. We’ll also be using constructor injection to inject for the LinkService that we still need to implement. I tend to prefer constructor injection this because this makes it more flexible when we are writing unit tests. It also makes it very clear which dependencies this class requires when it gets instantiated. If you want to differentiate between optional and required dependencies you can do this by using setter injection for the optional dependencies. This controller is basically just a pass through class, it will barely contain any logic at all, mostly just pass it data to the LinkService and return the correct HTTP Status and objects if needed.
In general I would never expose my database entities directly to my REST interface, but instead use domain transfer objects (DTO) or something like that to avoid coupling my exposed service with my database layer so both can change separately if needed but for demo purposes I’ll do it like this for now. Next up the more interesting class will be the LinkService, which will contain most of our business logic. First I will check if the requested URL is already in the database by one of the custom find methods we have implemented on the LinkRepository interface, if this is true we’ll just return the LinkEntity in the database. If we don’t have this URL in our database, we’ll create a short Base36 hash from this URL and create a LinkEntity from this hash and the URL. The resulting stored LinkEntity will be returned back to our REST endpoint.
You can see the logic for the creating the hash is extracted into a separate service since I want to be able to quickly plug another hashing mechanism when needed in the future. If this is needed I can just implement the interface and specify to another bean in this class without the need to change anything in the LinkService at all.
LinkService.java
Base36HashService.java
I’ve introduced a small support enum which contains the protocols I will support for now, which are http://
and https://
.
At this point we are able to run our Application.java class already to start up an embedded Tomcat. Before we do this I’ll make a quick change to the LinkEntity to avoid exposing fields to our REST client that aren’t necessary. This is easily done by adding an @JsonIgnore annotation to the fields you want to keep hidden. As you could see in the original LinkEntity source code, I already them there to all fields except hash and url. Now we are all set and you can run your application and try it out by doing a POST with your favorite REST client. I use a Chrome extension called Postman.
If you try to do a POST on http://localhost:8080/links?url=http://www.drissamri.com it will return the following response.
That’s our end to end flow for creating a hash. Let’s continue to actually find all persisted links in the database. This will be done by sending a GET request to the same URL. To do this we will extend the LinkService and LinkServiceImpl with an extra find method.
LinkService.java
LinkServiceImpl.java
Like I said before, I wouldn’t recommend exposing your database entities directly to your clients so usually you would want to map your Entity to some sort of Domain Transfer Object (DTO) which only purpose is to transfer data. You might wonder where the findAll() method comes from on the repository since we didn’t implement it on our interface, but this is one of the generated methods you can use by using Spring Data JPA. Awesome right?
Now the final step is to implement the the REST endpoint in the LinkController. We will retrieve all the links and return them to the client together with HTTP 200 OK status.
LinkController.java
By doing this you are able to POST an URL’s you want and afterwards find all the links stored in the database by doing a GET to http://localhost:8080/links
. That went smooth right.
As a final step let’s create an endpoint that will actually redirect us based on the hash we received. Lets make a RedirectController that is mapped to the root context and only takes a GET method and as a path variable the hash we create. So when we do a GET to http://localhost/{hash}
it will redirect us to a corresponding URL stored in the database.
First let us update the LinkService to have a method that can return the Link URL by its corresponding hash.
LinkService.java
LinkServiceImpl.java
As a little bonus we’ll redirect everyone that tries to redirect to a unknown hash to my own website, to make it easy. (wohoo!)
Redirecting hash to long URL
We will put this REST endpoint on a new controller, the RedirectController
. This time we won’t use the @RestController
since we don’t want to send back JSON Objects to the client but just do a redirect for a specific URL. Here we will use the traditional @Controller
annotation and map it on the root path of our application. We will only map the GET method and add a path variable that will contain the hash. By using redirect: in the return value, Spring knows will redirect to the given URL.
RedirectController.java
Security Considerations
It is important to know that URL shorteners are known to pose an extra security risk, users might try to spread malicious links using your service. One way to try to avoid this is by trying to validate the incoming URL with services like Google Safe Browsing (free), PhishTank (free) and BrightCloud (paid).
Let’s try to add validation to our Linkshortener with Google Safe Browsing and PhishTank because they are free. You should register to get a free API key though, or you’ll be very limited to the amount of requests you can do. You can do this by going to Google Developer Console and/or PhishTank API registration.
You can add the API keys to the application.properties file I have prepared for this application. I left them blank here since I do not want to make my API keys public.
Application.properties
Let us start by implementing the Google Safe Browsing API. We’ll make an interface that each URL verifier will have to implement.
We have some parameters as you can see in the property file, let’s bundle everything in a configuration class for the Google Verifier.
GoogleSafeBrowsingConfig.java
Next we’ll actually implement the validation of the URL, this class will use the UrlVerifier interface. We need to do a GET HTTP request to the Google API with certain query parameters as you’ve seen in the property file and configuration class. Spring has the Spring RestTemplate which makes it really easy to do HTTP requests and handle the result. When Google Safe Browsing thinks the URL we send is a phishing or malware site, it will return a HTTP 200 code. When this happens we will return false, meaning the link is unsafe.
Now we will do the same for the PhishTank API. We will make another configuration class to hold all the necessary properties.
The PhishTank API works a little bit different since we’ll need to do a HTTP POST, with form parameters instead of query parameters. The API will send back HTTP 200 and we’ll need to check the JSON body response to see if the service thinks the URL is malicious or not. We’ll only be checking one value in the JSON response, which is a boolean in_database. This boolean will tell us if the service knows this URL, which means it is submitted as a phishing link. This doesn’t mean it is actually verified already, so if we were to use this service we’d probably want to parse the response and take into account more properties from the result. The service could also send back HTTP 509 meaning that we have made too many requests, but we’ll take this and any other error response codes and just log a warning in the log files. When this happens, we will skip the verification and accept the link as safe for now.
To parse out the in_database fields in the JSON response, I’ve added the JSON Path library to the POM. This library makes it easy to query JSON like we would use XPath for XML documents.
pom.xml
Then we are set to actually implement the PhishTank URL verifier.
PhishTankUrlVerifier.java
Now we have two different URL verifiers, we want to use both of them for each request. Since we both implement the same UrlVerifier interface, if we make them Spring beans we can Autowire a List of the interface. We will do this in a seperate class that will loop over this List, so we can in the future add more verifiers if we would want to and it will check each one in the list to see if the URL we want to shorten is not a malicious link. The two URL Verifiers we have implemented both expect the URL to be encoded, so we’ll do this here as well.
UrlVerifiers.java
Now you might wonder, how can we Autowire the UrlVerifier if they are not annotated with @Component or something else? You are right, I’m running a bit ahead of my self. I still have to show you the last class to complete this section. We have a seperate @Configuration class that will wire the different UrlVerifiers and also the RestTemplate we have used to make the HTTP requests.
You see we first check if the API key for each of the services is populated in the application.properties
file, if that is not the case we will not create the UrlVerifier. If we want to add a new verifier in the future we just add another bean in this configuration file and make sure it implements the UrlVerifier interface and it will automatically be added to the list of verifiers that will be processed.
Great success
Tadaa! A very simple Linkshortener REST API. In the source code you’ll see we also implement a DELETE method, but this is pretty straight forward that I didn’t go into it here. You can take a look at the full source code at the GitHub repository.