目录结构:
root:[./shortly]
+--shortly.py
+--static
| +--style.css
+--templates
| +--layout.html
| +--new_url.html
| +--short_link_details.html
"""
Created on 2021/1/19 19:28
@author: allenshen2533
@desc: a simple werkzeug application
"""
import os
import redis
from urllib.parse import urlparse
from werkzeug.wrappers import Request, Response
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, NotFound
from werkzeug.middleware.shared_data import SharedDataMiddleware
from werkzeug.utils import redirect
from jinja2 import Environment, FileSystemLoader
from logging_test import logger
class Shortly(object):
def __init__(self, config):
self.redis = redis.Redis(config['redis_host'], config['redis_port'])
template_path = os.path.join(os.path.dirname(__file__), 'templates')
self.jinja_env = Environment(loader=FileSystemLoader(template_path),autoescape=True)
self.url_map = Map([
Rule('/', endpoint='new_url'),
Rule('/<short_id>', endpoint='follow_short_link'),
Rule('/<short_id>+', endpoint='short_link_details')
])
logger.debug("__init__ executed.")
def render_template(self, template_name, **context):
t = self.jinja_env.get_template(template_name)
logger.debug("render_template executed.")
context = convert(context)
return Response(t.render(context), mimetype='text/html')
def dispatch_request(self, request):
adapter = self.url_map.bind_to_environ(request.environ)
try:
endpoint, values = adapter.match()
print('endpoint---->', 'on_' + endpoint, 'values---->', values)
logger.debug("dispatch_request executed.")
return getattr(self, 'on_' + endpoint)(request, **values)
except HTTPException as e:
return e
def on_new_url(self, request):
error = None
url = ''
if request.method == 'POST':
url = request.form['url']
print('url----->', url)
if not is_valid_url(url):
error = 'Please enter a valid URL'
else:
short_id = self.insert_url(url)
logger.debug("on_new_url executed.")
return redirect('/%s+' % short_id)
return self.render_template('new_url.html', error=error, url=url)
def insert_url(self, url):
short_id = self.redis.get('reverse-url:' + url)
if short_id is not None:
return int(short_id) or short_id
url_num = self.redis.incr('last-url-id')
short_id = base36_encode(url_num)
self.redis.set('url-target:' + short_id, url)
self.redis.set('reverse-url:' + url, short_id)
logger.debug("insert_url executed.")
return int(short_id) or short_id
def on_follow_short_link(self, request, short_id):
link_target = self.redis.get('url-target:' + short_id)
if link_target is None:
raise NotFound()
self.redis.incr('click-count:' + short_id)
logger.debug("on_follow_short_link executed.")
return redirect(link_target)
def on_short_link_details(self, request, short_id):
link_target = self.redis.get('url-target:' + short_id)
if link_target is None:
raise NotFound()
click_count = int(self.redis.get('click-count:' + short_id) or 0)
logger.debug("on_short_link_details executed.")
return self.render_template('short_link_details.html',
link_target=link_target,
short_id=short_id,
click_count=click_count
)
def wsgi_app(self, environ, start_response):
request = Request(environ)
response = self.dispatch_request(request)
return response(environ, start_response)
def __call__(self, environ, start_response):
return self. wsgi_app(environ, start_response)
def is_valid_url(url):
parts = urlparse(url)
return parts.scheme in ('http', 'https')
def convert(data):
if isinstance(data, bytes): return data.decode('ascii')
if isinstance(data, dict): return dict(map(convert, data.items()))
if isinstance(data, tuple): return map(convert, data)
return data
def base36_encode(number):
assert number >= 0, 'positive integer required'
if number == 0:
return '0'
base36 = []
while number != 0:
number, i = divmod(number, 36)
base36.append('0123456789abcdefghijklmnopqrstuvwxyz'[i])
logger.debug('base36----------------->{}'.format(base36))
return ''.join(reversed(base36))
def create_app(redis_host='localhost', redis_port=6379, with_static=True):
app = Shortly({
'redis_host': redis_host,
'redis_port': redis_port
})
if with_static:
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/static': os.path.join(os.path.dirname(__file__), 'static')
})
return app
if __name__ == '__main__':
from werkzeug.serving import run_simple
app = create_app()
run_simple('127.0.0.1', 8080, app, use_debugger=True, use_reloader=True)