攝影師:ThisIsEngineering,連結:Pexels
哈囉,我們又見面了,上一篇 [原來後端要知道] 什麼是前後端分離? #前端後端是什麼 #軟體發展歷程 #軟體開發思維 我們解釋了前後端分離的和概念,這一篇要以實作來讓大家實際 感覺 一下,前後端分離之後,所帶來的好處。
拿上次的圖,修改成這次我們要實作的架構:「使用者可以透過各種裝置,訪問我們的網頁前端,而前端會透過 Web 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 的話,得到的回應是,也就是得到目前留言板裡面所有的發文
我們使用 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>
Github Page
上將上面的 index.html
,放到 Github
、並啟動 Github Page
,可以參考 將Vue專案部署至Github Pages,成為一個最簡潔的 Vue.js
的專案。
設定 Github Page
,後面的名稱會根據你的 Github
repository 來決定哦,因為我之前有在 Github Page 綁定網域了,所以我可以透過自訂網域,來連接到這個 Vue.js
專案。
最後呈現畫面,這留言板有很多怪怪的留言,所以就遮住了 XD
從上面的實作可以看到,後端歸後端、前端歸前端,兩者之間的聯繫,就只有薄薄的一層 Web API
的關係,另外,如果我今天還想把這個服務擴大到 Android
、iOS
也是沒問題的,在 Android
可以透過 Retrofit
套件,以簡單的方式接 Web API,客製化在 Android
的呈現方式,更或者,我乾脆把網頁寫好、兼具響應式網頁設計 (RWD),直接在 Android
嵌入 WebView
,也是可以。
總而言之,透過這次的實作,讓大家可以感覺到前後端分離的擴充威力。
我是 RS,這是我的 不做怎麼知道系列 文章,我們 下次見。
# 把資料庫的 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 語法的資源可以分享嗎?
RongSon (Sam Ho)
EPP都有說明壓
黃彥儒 搜嘎,那我真的要好好來研究這本了,感謝~~~~
RongSon (Sam Ho)
我找了一下,似乎是我記錯了
我建議你去看官方文件