iT邦幫忙

2

[原來後端要知道] 怎麼實作前後端分離? #Vue.js #Django #WebAPI

https://images.pexels.com/photos/3861958/pexels-photo-3861958.jpeg?auto=compress&cs=tinysrgb&h=750&w=1260

攝影師:ThisIsEngineering,連結:Pexels

哈囉,我們又見面了,上一篇 [原來後端要知道] 什麼是前後端分離? #前端後端是什麼 #軟體發展歷程 #軟體開發思維 我們解釋了前後端分離的和概念,這一篇要以實作來讓大家實際 感覺 一下,前後端分離之後,所帶來的好處。

拿上次的圖,修改成這次我們要實作的架構:「使用者可以透過各種裝置,訪問我們的網頁前端,而前端會透過 Web API 向後端拿資料,然後再呈現到網頁上給使用者看」。

https://ithelp.ithome.com.tw/upload/images/20200503/2012454816SEMyUT4S.png

1. 在後端開出 API

我們使用 Django(Python) 框架,開出一個簡單的 API,給前端接接看,這邊我們直接使用,我之前寫留言板所做的 GET post/ 的 API,也就是 讓前端能夠拿到留言板中所有的發文

urls.py

將 API 開在 http://my-server-domain/api/v1/board/post/,作為前端的接口。

from django.urls import path
from board.apis import post

api_path = 'api/v1/'

urlpatterns = [
	path(api_path + 'board/post/', post),
  path(api_path + 'board/post', post),
]

apis.py

透過 urls.py,將接口導到 board 模組的 apis.py 裡的 post() function,原本我有做 JWT token 來知道使用者是誰、還有其他抽象化的寫法,不過這邊 demo 用,我就把它簡化。

from django.views.decorators.csrf import csrf_exempt
from django.core.handlers.wsgi import WSGIRequest
from django.http import JsonResponse

@csrf_exempt
def post(request: WSGIRequest):
    if request.method == 'GET':
		    return JsonResponse({'status': 'Get posts succeed', 'posts': _get_all_posts()})

# 把資料庫的 Post 資料,全部挖出來、包成 dict 的格式出去
def _get_all_posts():
    # all posts
    all_posts = Post.objects.all()

    posts = []

    for _post in all_posts:
        post_data = {
            'post_id': _post.id,
            'author': str(_post.author),
            'created': _format_time(_post.created),
            'last_updated': _format_time(_post.last_updated),
            'content': _post.content,
            'liked': _post.liked,
            'comments_count': len(_all_comments),
            'comments': _all_comments,
        }
        posts.append(post_data)

    return posts

models.py

from django.db import models

class Post(models.Model):
    # related to User
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    # attributes
    content = models.fields.TextField(blank=True)
    liked = models.fields.PositiveIntegerField(default=0)

    created = models.fields.DateTimeField(default=timezone.now)
    last_updated = models.fields.DateTimeField(default=timezone.now)

    def __str__(self):
        return str(self.author) + ":" + str(self.content)

然後再把這支 Django 程式,放到雲端平台上執行、給個自己的網域,如果不知道怎麼做,我有 寫一系列的文章 告訴你怎麼做到,這邊就不贅述細節,總之,我在後端已經開出一支這樣的 API:http://my-domain.com/api/v1/board/post/(抱歉,這支的網域是亂寫的,如果大家都來打 API,我的 Server 應該會掛掉 XD)

如果用 Postman 丟這支 API 的話,得到的回應是,也就是得到目前留言板裡面所有的發文

https://ithelp.ithome.com.tw/upload/images/20200503/20124548CuwMnnMJn6.png

2. 在前端接 API

我們使用 Vue.js 前端框架,試著從後端拿到發文的資料看看。

index.html

html 中,使用 script tag 可以執行 javascript,再使用 fetch() 來發送 API、接收 response。

<div id="app">
	<ul>
		<li v-for="post in posts">
			{{ post }}
		</li>
	</ul>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
	const app = new Vue({
		el: '#app',
		data: {
			posts: []
		},
		created () {
			fetch('http://my-domain.com/api/v1/board/post/')
			.then(response => response.json())
			.then(json => {
				this.posts = json.posts
			})
		}
	})
</script>

3. 再將前端網頁放到 Github Page

將上面的 index.html,放到 Github、並啟動 Github Page,可以參考 將Vue專案部署至Github Pages,成為一個最簡潔的 Vue.js 的專案。

https://ithelp.ithome.com.tw/upload/images/20200503/20124548miVlORTjcx.png

設定 Github Page,後面的名稱會根據你的 Github repository 來決定哦,因為我之前有在 Github Page 綁定網域了,所以我可以透過自訂網域,來連接到這個 Vue.js 專案。

https://ithelp.ithome.com.tw/upload/images/20200503/20124548dnJcR2wEEX.png

最後呈現畫面,這留言板有很多怪怪的留言,所以就遮住了 XD

https://ithelp.ithome.com.tw/upload/images/20200503/20124548jRqylHSbIA.png

總結

從上面的實作可以看到,後端歸後端、前端歸前端,兩者之間的聯繫,就只有薄薄的一層 Web API 的關係,另外,如果我今天還想把這個服務擴大到 AndroidiOS 也是沒問題的,在 Android 可以透過 Retrofit 套件,以簡單的方式接 Web API,客製化在 Android 的呈現方式,更或者,我乾脆把網頁寫好、兼具響應式網頁設計 (RWD),直接在 Android 嵌入 WebView,也是可以。

總而言之,透過這次的實作,讓大家可以感覺到前後端分離的擴充威力。

我是 RS,這是我的 不做怎麼知道系列 文章,我們 下次見。



1 則留言

1
黃彥儒
iT邦高手 1 級 ‧ 2020-05-03 23:19:00
# 把資料庫的 Post 資料,全部挖出來、包成 dict 的格式出去
def _get_all_posts():
    # all posts
    all_posts = Post.objects.all()

    posts = []

    for _post in all_posts:
        post_data = {
            'post_id': _post.id,
            'author': str(_post.author),
            'created': _format_time(_post.created),
            'last_updated': _format_time(_post.last_updated),
            'content': _post.content,
            'liked': _post.liked,
            'comments_count': len(_all_comments),
            'comments': _all_comments,
        }
        posts.append(post_data)

    return posts

等價於

# 把資料庫的 Post 資料,全部挖出來、包成 dict 的格式出去
def _get_all_posts():
    return [
        {
            'post_id': _post.id,
            'author': str(_post.author),
            'created': _format_time(_post.created),
            'last_updated': _format_time(_post.last_updated),
            'content': _post.content,
            'liked': _post.liked,
            'comments_count': len(_all_comments),
            'comments': _all_comments,
        } for _post in Post.objects.all()
    ]

所以

@csrf_exempt
def post(request: WSGIRequest):
    if request.method == 'GET':
        return JsonResponse(
            {
                'status': 'Get posts succeed',
                'posts': [{
                    'post_id': _post.id,
                    'author': str(_post.author),
                    'created': _format_time(_post.created),
                    'last_updated': _format_time(_post.last_updated),
                    'content': _post.content,
                    'liked': _post.liked,
                    'comments_count': len(_all_comments),
                    'comments': _all_comments,
                } for _post in Post.objects.all()]
            })
看更多先前的回應...收起先前的回應...

啊! 搜嘎
我現在還是用 Java 的概念來寫 Python
感謝分享!

想請問有更多像這種 python 語法的資源可以分享嗎?

黃彥儒 iT邦高手 1 級 ‧ 2020-05-04 12:42:01 檢舉

RongSon (Sam Ho)
EPP都有說明壓

黃彥儒 搜嘎,那我真的要好好來研究這本了,感謝~~~~

黃彥儒 iT邦高手 1 級 ‧ 2020-05-06 13:44:32 檢舉

RongSon (Sam Ho)
我找了一下,似乎是我記錯了
我建議你去看官方文件

黃彥儒 你是說 python doc 嗎? 還是 PEP8 之類的文件?

黃彥儒 iT邦高手 1 級 ‧ 2020-05-07 22:24:58 檢舉

對!!!一直想不起來/images/emoticon/emoticon13.gif

搜嘎,感謝提醒~~~

我要留言

立即登入留言