A Simple Werkzeug Application

这是一个使用Werkzeug和Redis实现的简单短链接服务应用。它包括创建新URL、跟随短链接和查看短链接详情的功能。通过Redis进行键值存储,将长URL映射到短ID,并记录点击次数。应用还使用了Jinja2模板引擎来渲染HTML页面,并提供了静态文件服务。
摘要由CSDN通过智能技术生成
目录结构:
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('values类型...', type(values))
            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)
        # logger.debug('---------------------------------------------------')
        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)
        # logger.debug('link_target.....{}'.format(link_target))
        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): 
# 将字典的键&值从byte类型转换为str类型, 
# str 使用 encode() 方法转换成 byte ,byte 使用 decode 方法转变成 str
    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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值