HTTP Redirects - Simplify controller logic, through separation of concerns

Morgan Hallgren
4 min readJul 5, 2016

--

The HTTP 3xx status codes indicates that the client must take additional action to complete a request. It was often used in the good old days to redirect a user to a landing page after a successful login.

In this post I will give redirect some light and try to express how it could help your make your web application simpler.

Example application

Lets say the application we are building has a tree structure containing regions and cities where a city is placed below a region but is in itself an own entity in the application.

The following URI´s are valid for the application.

  • /region — list of regions
  • /region/:region_id — one region
  • /city — list of cities
  • /city/:city_id — one city

The application also have users that have access to either one or more regions or one or more cities. When a user enters the application she should be granted access to her region(s) or city(s) based on her settings.

When a GET / request is made the response should be a link to the region(s) or city(s) URI based on the user settings.

REQUEST

GET  /

RESPONSE

<a rel="self" href="/">/</a><a rel="level" href="/region_or_city_uri">/region_or_city_uri</a>

The rel attribute specifies the relationship between the current document and the linked document and the href is the URI to the document.

The response includes two links [self and level] self is a link to the requested document and level a link to region(s) or city(s) based on the users settings.

A common way of construct the level URI is to access the user setting in the GET / controller and respond with a level URI that point to the correct level.

If the user has access to region sweden the level URI would look like this.

<a rel="level" href="/region/sweden">/region/sweden</a>

And if the user has access to the city of Lund and no region access.

<a rel="level" href="/city/lund">/city/lund</a>

The GET / controller is responsible for fetching the user settings for levels and construct the level link. If we follow this pattern for later to come links the GET / controller may have to fetch data from other resources making it harder to grasp and maintain.

Redirect

Another approach is to respond with a static URI for the level rel in the GET / controller.

<a rel="self" href="/">/</a>
<a rel="level" href="/level">/level</a>

The user settings lookup are now moved from the GET / controller to the GET /level controller. When /level is requested the controller checks the user settings and redirect to a /region or /city link based on the setting. If the user don´t have access to a region or city the status code 403 Forbidden is returned.

get ‘/level’ do
redirect “/region” if region_access?
redirect “/city” if city_access?
halt 403, “no access (region / city)”
end

The /region and /city GET controllers.

get ‘/region’ do
if region_access?
regions = get_regions_based_on_user_setting()
render(regions) if regions.length > 1
redirect "/region/#{regions.first}"
else
halt 403, "no access (region)"
end
end
get ‘/city’ do
if city_access?
cities = get_cities_based_on_user_setting()
render(cities) if cities.length > 1
redirect "/city/#{cities.first}"
else
halt 403, "no access (city)"
end
end
  • First the controllers check that the requested user has region or city access.
  • Fetching the regions or cities.
  • Respond via the render() function.

But wait, the controllers are redirecting the request to /region/:region_id or /city/:city_id if there is only one region or city connected to the user.

get ‘/region/:region_id’ do
if region_access?
region = get_region_based_on_user_setting(params[:region_id])
render(region)
else
halt 403, "no access (region)"
end
end
get ‘/city/:city_id’ do
if city_access?
city = get_city_based_on_user_setting(params[:city_id])
render(city)
else
halt 403, "no access (city)"
end
end

What we get is a more decoupled application where the logic is where it belongs. It´s easier to link to the level link in other parts of the application when no logic is needed for the static link /level.

Pros

  • Put logic where it belongs / Less messy controllers
  • Make decision as late as possible.
  • Faster and static root controllers.

Cons

  • Many requests between the client and the server.
  • Slower response times.

Example application setup

To get more understanding in how this application can be setup. I´ll try to sum it up in this section.

Regions

Sweden / Denmark

Cities

Lund / Malmö / Copenhagen

Regions / City Setup

The regions and city connections

/region/sweden
/city/lund
/city/malmö
/region/denmark
/city/copenhagen

Users

Elna / Marie / Klas / Sven

Elna has access to both region Sweden and Denmark and the /level link will redirect her to /region. The length of regions is higher than one and the response will be a list of the two regions.

Marie has access to only region Sweden and the /level link will redirect her to /region. In Marie´s case the length of regions are one and she will be redirected once more to /region/sweden. The render function for a region will probably hold some region information but also a list of city links to /city/lund and /city/malmö

Klas has access to both city Malmö and Lund and the /level link will redirect him to /city that has the same logic as /region and the two cities will be rendered in the response.

Sven has access to only the of city Denmark and will have the same two redirects as Marie for /regions but /city/denmark will not have any more links further as /region/sweden had.

End notes

This thinking is heavily inspired by the ROCA style guides that is highly recommended for anyone creating web applications.

--

--