Skip to content

Instantly share code, notes, and snippets.

@georgewhewell
Last active September 13, 2018 19:51
Show Gist options
  • Save georgewhewell/6932b00b2b12baad245914024d2bcb46 to your computer and use it in GitHub Desktop.
Save georgewhewell/6932b00b2b12baad245914024d2bcb46 to your computer and use it in GitHub Desktop.
A simple URL shortening service

URL Shortener

This is a simple URL shortening service, written in Python using Flask and Redis.

Run

$ nix-shell -p python3.pkgs.flask python3.pkgs.redis
[nix-shell:~/src/shortener]$ FLASK_APP=app.py flask run
 * Serving Flask app "app.py"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Add URL

$ curl -XPOST -d '{"url": "http://google.com"}' -H "Content-Type: application/json" localhost:5000/shorten_url
{"shortened_url":"http://localhost:5000/nZXzVO7i"}

Get URL

$ curl http://localhost:5000/nZXzVO7i
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="b'http://google.com'">b'http://google.com'</a>.  If not click the link.

Check Validation

$ curl -XPOST -d '{"url": "notaurl"}' -H "Content-Type: application/json" localhost:5000/shorten_url
{"error":"please supply absolute url"}
import random
import string
from flask import Flask, abort, request, jsonify, redirect, make_response
from redis import Redis
from urllib.parse import urlparse
app = Flask(__name__)
redis = Redis()
MY_SERVICE = 'http://localhost:5000'
MYSELF_PARSED = urlparse(MY_SERVICE)
SHORTENED_LENGTH = 8
ALLOWED_CHARACTERS = string.ascii_letters + string.digits
def _abort_code(code, message):
abort(make_response(jsonify(error=message), code))
def _generate_code():
return "".join(random.choice(ALLOWED_CHARACTERS) for _ in range(SHORTENED_LENGTH))
def _get_code_for(url):
code = _generate_code()
while redis.get(code):
code = _generate_code()
redis.set(code, url)
return code
def _get_url_for(code):
return redis.get(code) or _abort_code(404, 'not found')
def _get_validated_url(data):
try:
parsed = urlparse(data['url'])
except KeyError:
_abort_code(400, 'please supply url attribute')
if not all([parsed.scheme, parsed.netloc]):
_abort_code(400, 'please supply absolute url')
if parsed.netloc == MYSELF_PARSED.netloc:
_abort_code(400, 'cannot shorten own url')
return data['url']
@app.route('/shorten_url', methods=['POST'])
def handle_create():
if not request.is_json:
_abort_code(400, 'please supply json')
url = _get_validated_url(request.json)
code = _get_code_for(url)
return jsonify(shortened_url=(MY_SERVICE + '/' + code)), 201
@app.route('/<code>', methods=['GET'])
def handle_lookup(code):
return redirect(
_get_url_for(code)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment