Python版——博客網站<二>構建前端編寫API

開源地址:https://github.com/leebingbin/Python3.WebAPP.Blogcss

 

    至此,ORM框架、Web框架和配置都已就緒,咱們能夠開始編寫一個最簡單的MVC,把它們所有啓動起來。html

    經過Web框架的@get和ORM框架的Model支持,能夠很容易地編寫一個處理首頁URL的函數:前端

 

    '__template__'指定的模板文件是test.html,其餘參數是傳遞給模板的數據,因此咱們在模板的根目錄templates下建立test.html:vue

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Test Users - Python3.WebAPP.BlogWebapp</title>
</head>
<body>
    <h1>All users</h1>
    {% for u in users %}
    <p>{{ u.name }} / {{ u.email }}</p>
    {% endfor %}
</body>
</html>

    接下來,能夠用命令行啓動Web服務器:html5

$ python3 app.py

    在瀏覽器中訪問http://localhost:9000/。若是數據庫的users表什麼內容也沒有,你就沒法在瀏覽器中看到循環輸出的內容。能夠本身在MySQL的命令行裏給users表添加幾條記錄,而後再訪問,j就能夠看到了:python

 

    對於複雜的HTML前端頁面來講,咱們須要一套基礎的CSS框架來完成頁面佈局和基本樣式。另外,jQuery做爲操做DOM的JavaScript庫也必不可少。從零開始寫CSS不如直接從一個已有的功能完善的CSS框架開始。有不少CSS框架可供選擇。咱們此次選擇uikit這個強大的CSS框架。它具有完善的響應式佈局,漂亮的UI,以及豐富的HTML組件,讓咱們能輕鬆設計出美觀而簡潔的頁面。jquery

    能夠從uikit ( https://getuikit.com/ )首頁下載打包的資源文件。git

    全部的靜態資源文件咱們統一放到www/static目錄下,並按照類別歸類:github

static/
+- css/
|  +- addons/
|  |  +- uikit.addons.min.css
|  |  +- uikit.almost-flat.addons.min.css
|  |  +- uikit.gradient.addons.min.css
|  +- python3.webapp.blog.css
|  +- uikit.almost-flat.min.css
|  +- uikit.gradient.min.css
|  +- uikit.min.css
+- fonts/
|  +- fontaBlogAPP-webfont.ttf
|  +- fontaBlogAPP-webfont.woff
|  +- FontBlogAPP.otf
|  +- fontBlogAPP-webfont.eot
+- img/
|  +- user.png
+- js/
|  +- jquery.min.js
|  +- python3.webapp.blog.js
|  +- sha1.min.js
|  +- sticky.min.js
|  +- uikit.min.js
|  +- uikit-icons.min.js
|  +- vue.min.js

    因爲前端頁面確定不止首頁一個頁面,每一個頁面都有相同的頁眉和頁腳。若是每一個頁面都是獨立的HTML模板,那麼咱們在修改頁眉和頁腳的時候,就須要把每一個模板都改一遍,這顯然是沒有效率的。常見的模板引擎已經考慮到了頁面上重複的HTML部分的複用問題。有的模板經過include把頁面拆成三部分:web

<html>
    <% include file="inc_header.html" %>
    <% include file="index_body.html" %>
    <% include file="inc_footer.html" %>
</html>

    這樣,相同的部分inc_header.html和inc_footer.html就能夠共享。

    可是include方法不利於頁面總體結構的維護。jinjia2的模板還有另外一種「繼承」方式,實現模板的複用更簡單。「繼承」模板的方式是經過編寫一個「父模板」,在父模板中定義一些可替換的block(塊)。而後,編寫多個「子模板」,每一個子模板均可以只替換父模板定義的block。好比,定義一個最簡單的父模板,這樣,一旦定義好父模板的總體佈局和CSS樣式,編寫子模板就會很是容易。

    讓咱們經過uikit這個CSS框架來完成父模板 __base__.html 的編寫:

<!DOCTYPE html>
<!--
{% macro pagination(url, page) %}
    <ul class="uk-pagination">
        {% if page.has_previous %}
            <li><a href="{{ url }}{{ page.page_index - 1 }}"><i class="uk-icon-angle-double-left"></i></a></li>
        {% else %}
            <li class="uk-disabled"><span><i class="uk-icon-angle-double-left"></i></span></li>
        {% endif %}
            <li class="uk-active"><span>{{ page.page_index }}</span></li>
        {% if page.has_next %}
            <li><a href="{{ url }}{{ page.page_index + 1 }}"><i class="uk-icon-angle-double-right"></i></a></li>
        {% else %}
            <li class="uk-disabled"><span><i class="uk-icon-angle-double-right"></i></span></li>
        {% endif %}
    </ul>
{% endmacro %}
-->
<html>
<head>
    <meta charset="utf-8" />
    {% block meta %}<!-- block meta  -->{% endblock %}
    <title>{% block title %} ? {% endblock %} -  Python Blog Webapp</title>
    <link rel="stylesheet" href="/static/css/uikit.min.css">
    <link rel="stylesheet" href="/static/css/uikit.gradient.min.css">
    <link rel="stylesheet" href="/static/css/python3.webapp.blog.css"/>
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/sha1.min.js"></script>
    <script src="/static/js/uikit.min.js"></script>
    <script src="/static/js/sticky.min.js"></script>
    <script src="/static/js/vue.min.js"></script>
    <script src="/static/js/python3.webapp.blog.js"></script>
    {% block beforehead %}<!-- before head  -->{% endblock %}
</head>
<body>
    <nav class="uk-navbar uk-navbar-attached uk-margin-bottom">
        <div class="uk-container uk-container-center">
            <a href="/" class="uk-navbar-brand">Blog</a>
            <ul class="uk-navbar-nav">
                <li data-url="blogs"><a href="/"><i class="uk-icon-home"></i> 日誌</a></li>
                <li><a target="_blank" href="https://my.oschina.net/u/3375733/blog?catalog=5663972&temp=1507368711950"><i class="uk-icon-book"></i> 教程</a></li>
                <li><a target="_blank" href="https://github.com/leebingbin/Python3.WebAPP.Blog"><i class="uk-icon-code"></i> 源碼</a></li>
            </ul>
            <div class="uk-navbar-flip">
                <ul class="uk-navbar-nav">
                {% if __user__ %}
                    <li class="uk-parent" data-uk-dropdown>
                        <a href="#0"><i class="uk-icon-user"></i> {{ __user__.name }}</a>
                        <div class="uk-dropdown uk-dropdown-navbar">
                            <ul class="uk-nav uk-nav-navbar">
                                <li><a href="/signout"><i class="uk-icon-sign-out"></i> 登出</a></li>
                            </ul>
                        </div>
                    </li>
                {% else %}
                    <li><a href="/signin"><i class="uk-icon-sign-in"></i> 登錄</a></li>
                    <li><a href="/register"><i class="uk-icon-edit"></i> 註冊</a></li>
                {% endif %}
                </ul>
            </div>
        </div>
    </nav>

    <div class="uk-container uk-container-center">
        <div class="uk-grid">
            <!-- content -->
            {% block content %}
            {% endblock %}
            <!-- // content -->
        </div>
    </div>

    <div class="uk-margin-large-top" style="background-color:#eee; border-top:1px solid #ccc;">
        <div class="uk-container uk-container-center uk-text-center">
            <div class="uk-panel uk-margin-top uk-margin-bottom">
                <p>
                    <a target="_blank" href="http://weibo.com/bingbinlee" class="uk-icon-button uk-icon-weibo"></a>
                    <a target="_blank" href="https://github.com/leebingbin" class="uk-icon-button uk-icon-github"></a>
                    <a target="_blank" href="http://www.linkedin.com/in/libingbin" class="uk-icon-button uk-icon-linkedin-square"></a>
                </p>
                <p>Powered by <a href="https://github.com/leebingbin/Python3.WebAPP.Blog">Python Blog Webapp</a>. Copyright &copy; 2016. [<a href="/manage/" target="_blank">Manage</a>]</p>
                <p><a href="https://my.oschina.net/u/3375733/blog" target="_blank">www.bingbinlee.com</a>. All rights reserved.</p>
                <a target="_blank" href="http://www.w3.org/TR/html5/"><i class="uk-icon-html5" style="font-size:64px; color: #444;"></i></a>
            </div>
        </div>
    </div>
</body>
</html>

    __base__.html定義的幾個block做用以下:

    用於子頁面定義一些meta,例如rss feed:

{% block meta %} ... {% endblock %}


    覆蓋頁面的標題:

{% block title %} ... {% endblock %}


    子頁面能夠在<head>標籤關閉前插入JavaScript代碼:

{% block beforehead %} ... {% endblock %}


    子頁面的content佈局和內容:

{% block content %}
    ...
{% endblock %}


    咱們把首頁改造一下,從 __base__.html 繼承一個 blogs.html :

{% extends '__base__.html' %}

{% block title %}日誌{% endblock %}

{% block beforehead %}

<script>
</script>

{% endblock %}

{% block content %}

    <div class="uk-width-medium-3-4">
    {% for blog in blogs %}
        <article class="uk-article">
            <h2><a href="/blog/{{ blog.id }}">{{ blog.name }}</a></h2>
            <p class="uk-article-meta">發表於{{ blog.created_at|datetime }}</p>
            <p>{{ blog.summary }}</p>
            <p><a href="/blog/{{ blog.id }}">繼續閱讀 <i class="uk-icon-angle-double-right"></i></a></p>
        </article>
        <hr class="uk-article-divider">
    {% endfor %}
    </div>

    <div class="uk-width-medium-1-4">
        <div class="uk-panel uk-panel-header">
            <h3 class="uk-panel-title">友情連接</h3>
            <ul class="uk-list uk-list-line">
                <li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="http://www.imooc.com/">編程</a></li>
                <li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="http://weread.qq.com/">讀書</a></li>
                <li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="https://my.oschina.net/u/3375733/blog?catalog=5663972&temp=1507368711950">Python教程</a></li>
                <li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="https://github.com/">GitHub</a></li>
            </ul>
        </div>
    </div>

{% endblock %}

    接下來,和以前測試的test.html同樣,也能夠用命令行啓動Web服務器。

    自從Roy Fielding博士在2000年他的博士論文中提出REST(Representational State Transfer)風格的軟件架構模式後,REST就基本上迅速取代了複雜而笨重的SOAP,成爲Web API的標準了。什麼是Web API呢?

    若是咱們想要獲取一篇Blog,輸入http://localhost:9000/blog/789,就能夠看到id爲789的Blog頁面,但這個結果是HTML頁面,它同時混合包含了Blog的數據和Blog的展現兩個部分。對於用戶來講,閱讀起來沒有問題,可是,若是機器讀取,就很難從HTML中解析出Blog的數據。

    若是一個URL返回的不是HTML,而是機器能直接解析的數據,這個URL就能夠當作是一個Web API。好比,讀取http://localhost:9000/api/blogs/789,若是能直接返回Blog的數據,那麼機器就能夠直接讀取。

    REST就是一種設計API的模式。最經常使用的數據格式是JSON。因爲JSON能直接被JavaScript讀取,因此,以JSON格式編寫的REST風格的API具備簡單、易讀、易用的特色。

    編寫API有什麼好處呢?因爲API就是把Web App的功能所有封裝了,因此,經過API操做數據,能夠極大地把前端和後端的代碼隔離,使得後端代碼易於測試,前端代碼編寫更簡單。

    一個API也是一個URL的處理函數,咱們但願能直接經過一個@api來把函數變成JSON格式的REST API,這樣,獲取註冊用戶能夠用一個API實現以下:

@get('/api/users')
def api_get_users():
    users = yield from User.findAll(orderBy='created_at desc')
    for u in users:
        u.passwd = '******'
    return dict(users=users)

    只要返回一個dict,後續的response這個middleware就能夠把結果序列化爲JSON並返回。咱們須要對Error進行處理,所以定義一個APIError,這種Error是指API調用時發生了邏輯錯誤(好比用戶不存在),其餘的Error視爲Bug,返回的錯誤代碼爲internalerror。

    客戶端調用API時,必須經過錯誤代碼來區分API調用是否成功。錯誤代碼是用來告訴調用者出錯的緣由。不少API用一個整數表示錯誤碼,這種方式很難維護錯誤碼,客戶端拿到錯誤碼還須要查表得知錯誤信息。更好的方式是用字符串表示錯誤代碼,不須要看文檔也能猜到錯誤緣由。

    能夠在瀏覽器直接測試API,例如,輸入http://localhost:9000/api/users,就能夠看到返回的JSON:

{
  "users":[
      {
        "name": "bingbinlee",
        "image": "http://www.gravatar.com/avatar/000000000000000000000000000000?dmm&size=120}",
        "admin": 1,
        "created_at": 1405684379.288,
        "password": "******",
        "email": "libingbin2015@aliyun.com",
        "id": "e807f1fcf82d132f9bb018ca6738a19f"
      }
    ]
}

 

本文爲博主原創文章,轉載請註明出處!

https://my.oschina.net/u/3375733/blog/

相關文章
相關標籤/搜索