Thứ tư, 24/10/2018 | 00:00 GMT+7

Cách gửi thông báo đẩy web từ ứng dụng Django

Web không ngừng phát triển và giờ đây nó có thể đạt được các chức năng mà trước đây chỉ có trên các thiết bị di động root . Sự ra đời của các nhân viên dịch vụ JavaScript đã mang lại cho web những khả năng mới được tìm thấy để thực hiện những việc như đồng bộ hóa nền, bộ nhớ đệm offline và gửi thông báo đẩy .

Thông báo đẩy cho phép user chọn nhận các bản cập nhật cho các ứng dụng web và di động. Chúng cũng cho phép user tương tác lại với các ứng dụng hiện có bằng cách sử dụng nội dung phù hợp và tùy chỉnh.

Trong hướng dẫn này, bạn sẽ cài đặt ứng dụng Django trên Ubuntu 18.04 để gửi thông báo đẩy khi nào có hoạt động yêu cầu user truy cập ứng dụng. Để tạo các thông báo này, bạn sẽ sử dụng gói Django-Webpush và cài đặt và đăng ký một nhân viên dịch vụ để hiển thị thông báo cho client . Ứng dụng đang hoạt động với các thông báo sẽ như sau:

Cuối cùng đẩy web

Yêu cầu

Trước khi bắt đầu hướng dẫn này, bạn cần những thứ sau:

Bước 1 - Cài đặt Django-Webpush và lấy các phím Vapid

Django-Webpush là một gói cho phép các nhà phát triển tích hợp và gửi thông báo đẩy web trong các ứng dụng Django. Ta sẽ sử dụng gói này để kích hoạt và gửi thông báo đẩy từ ứng dụng của bạn . Trong bước này, bạn sẽ cài đặt Django-Webpush và lấy các khóa Nhận dạng Server Ứng dụng Tự nguyện (VAPID) cần thiết để xác định server của bạn và đảm bảo tính duy nhất của từng yêu cầu.

Đảm bảo rằng bạn đang ở trong folder dự án ~/ djangopush mà bạn đã tạo trong yêu cầu :

  • cd ~/djangopush

Kích hoạt môi trường ảo của bạn:

  • source my_env/bin/activate

Nâng cấp version pip của bạn đảm bảo nó được cập nhật:

  • pip install --upgrade pip

Cài đặt Django-Webpush:

  • pip install django-webpush

Sau khi cài đặt gói, hãy thêm gói vào danh sách ứng dụng trong file settings.py của bạn. Đầu tiên hãy mở settings.py :

  • nano ~/djangopush/djangopush/settings.py

Thêm webpush vào danh sách INSTALLED_APPS :

~ / djangopush / djangopush / settings.py
...  INSTALLED_APPS = [     ...,     'webpush', ] ... 

Lưu file và thoát khỏi editor .

Chạy di chuyển trên ứng dụng để áp dụng các thay đổi bạn đã thực hiện cho giản đồ database của bạn :

  • python manage.py migrate

Đầu ra sẽ giống như thế này, cho biết một quá trình di chuyển thành công:

Output
Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, webpush Running migrations: Applying webpush.0001_initial... OK

Bước tiếp theo trong việc cài đặt thông báo đẩy trên web là nhận các khóa VAPID. Các khóa này xác định server ứng dụng và được dùng để giảm bí mật cho các URL đăng ký đẩy, vì chúng hạn chế đăng ký đối với một server cụ thể.

Để lấy các khóa VAPID, hãy chuyển đến ứng dụng web wep-push-codelab . Tại đây, bạn sẽ nhận được các khóa được tạo tự động. Sao chép private key và public key .

Tiếp theo, tạo một mục mới trong settings.py cho thông tin VAPID của bạn. Đầu tiên, hãy mở file :

  • nano ~/djangopush/djangopush/settings.py

Tiếp theo, thêm chỉ thị mới có tên WEBPUSH_SETTINGS với khóa public key và private key VAPID và email của bạn bên dưới AUTH_PASSWORD_VALIDATORS :

~ / djangopush / djangopush / settings.py
...  AUTH_PASSWORD_VALIDATORS = [     ... ]  WEBPUSH_SETTINGS = {    "VAPID_PUBLIC_KEY": "your_vapid_public_key",    "VAPID_PRIVATE_KEY": "your_vapid_private_key",    "VAPID_ADMIN_EMAIL": "admin@example.com" }  # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/  ... 

Đừng quên để thay thế các giá trị giữ chỗ your_vapid_public_key , your_vapid_private_key , và admin@example.com với thông tin của bạn . Địa chỉ email của bạn là cách bạn sẽ được thông báo nếu server đẩy gặp sự cố nào .

Tiếp theo, ta sẽ cài đặt các chế độ xem sẽ hiển thị trang chủ của ứng dụng và kích hoạt thông báo đẩy cho user đã đăng ký.

Bước 2 - Cài đặt chế độ xem

Trong bước này, ta sẽ cài đặt chế độ xem home cơ bản với đối tượng phản hồi HttpResponse cho trang chủ của ta , cùng với chế độ xem send_push . Chế độ xem là các hàm trả về các đối tượng phản hồi từ các yêu cầu web. Chế độ xem send_push sẽ sử dụng thư viện Django-Webpush để gửi các thông báo đẩy có chứa dữ liệu do user nhập trên trang chủ.

Điều hướng đến folder ~/djangopush/djangopush :

  • cd ~/djangopush/djangopush

Chạy ls bên trong folder sẽ hiển thị cho bạn các file chính của dự án:

Output
/__init__.py /settings.py /urls.py /wsgi.py

Các file trong folder này được tạo tự động bởi tiện ích django-admin mà bạn đã sử dụng để tạo dự án của bạn trong yêu cầu . Tệp settings.py chứa các cấu hình cho toàn dự án như các ứng dụng đã cài đặt và folder root tĩnh. Tệp urls.py chứa các cấu hình URL cho dự án. Đây là nơi bạn sẽ cài đặt các tuyến để phù hợp với các chế độ xem đã tạo của bạn.

Tạo một file mới bên trong folder ~/djangopush/djangopush được gọi là views.py , file này sẽ chứa các chế độ xem cho dự án của bạn:

  • nano ~/djangopush/djangopush/views.py

Chế độ xem đầu tiên mà ta sẽ thực hiện là chế độ xem home , sẽ hiển thị trang chủ nơi user có thể gửi thông báo đẩy. Thêm mã sau vào file :

~ / djangopush / djangopush / views.py
from django.http.response import HttpResponse from django.views.decorators.http import require_GET  @require_GET def home(request):     return HttpResponse('<h1>Home Page<h1>') 

Các home xem được trang trí bởi các require_GET trang trí, trong đó hạn chế chế độ xem để chỉ các yêu cầu GET. Một chế độ xem thường trả về một phản hồi cho mọi yêu cầu được thực hiện với nó. Chế độ xem này trả về một thẻ HTML đơn giản dưới dạng phản hồi.

Chế độ xem tiếp theo mà ta sẽ tạo là send_push , sẽ xử lý các thông báo đẩy đã gửi bằng gói django-webpush . Nó sẽ bị hạn chế chỉ đối với các yêu cầu POST và sẽ được miễn trừ khỏi chế độ bảo vệ Truy vấn Yêu cầu Trên Trang web (CSRF) . Làm điều này sẽ cho phép bạn kiểm tra chế độ xem bằng Postman hoặc bất kỳ dịch vụ RESTful nào khác. Tuy nhiên, trong quá trình production , bạn nên xóa phần trang trí này để tránh làm cho chế độ xem của bạn dễ bị CSRF.

Để tạo chế độ xem send_push , trước tiên hãy thêm các lần nhập sau để bật phản hồi JSON và truy cập vào chức năng send_user_notification trong thư viện webpush :

~ / djangopush / djangopush / views.py
from django.http.response import JsonResponse, HttpResponse from django.views.decorators.http import require_GET, require_POST from django.shortcuts import get_object_or_404 from django.contrib.auth.models import User from django.views.decorators.csrf import csrf_exempt from webpush import send_user_notification import json 

Tiếp theo, thêm require_POST trang trí, mà sẽ sử dụng cơ thể yêu cầu gửi bởi người sử dụng để tạo ra và kích hoạt một thông báo push:

~ / djangopush / djangopush / views.py
@require_GET def home(request):     ...   @require_POST @csrf_exempt def send_push(request):     try:         body = request.body         data = json.loads(body)          if 'head' not in data or 'body' not in data or 'id' not in data:             return JsonResponse(status=400, data={"message": "Invalid data format"})          user_id = data['id']         user = get_object_or_404(User, pk=user_id)         payload = {'head': data['head'], 'body': data['body']}         send_user_notification(user=user, payload=payload, ttl=1000)          return JsonResponse(status=200, data={"message": "Web push successful"})     except TypeError:         return JsonResponse(status=500, data={"message": "An error occurred"}) 

Ta đang sử dụng hai trang trí cho send_push xem: các require_POST trang trí, trong đó hạn chế chế độ xem để POST chỉ yêu cầu, và csrf_exempt trang trí, mà miễn trừ nhìn từ bảo vệ CSRF.

Chế độ xem này mong đợi dữ liệu POST và thực hiện những điều sau: nó nhận phần body của yêu cầu và sử dụng gói json , giải mã hóa tài liệu JSON thành một đối tượng Python bằng cách sử dụng json.loads . json.loads nhận một tài liệu JSON có cấu trúc và chuyển đổi nó thành một đối tượng Python.

Khung nhìn mong đợi đối tượng nội dung yêu cầu có ba thuộc tính:

  • head : Tiêu đề của thông báo đẩy.
  • body : Nội dung của thông báo.
  • id : id của user yêu cầu.

Nếu thiếu bất kỳ thuộc tính bắt buộc nào, chế độ xem sẽ trả về một JSONResponse với trạng thái 404 “Không tìm thấy”. Nếu user có khóa chính đã cho tồn tại, chế độ xem sẽ trả về user có khóa chính phù hợp bằng cách sử dụng hàm get_object_or_404 từ thư viện django.shortcuts . Nếu user không tồn tại, hàm sẽ trả về lỗi 404.

Chế độ xem cũng sử dụng chức năng send_user_notification từ thư viện webpush . Hàm này nhận ba tham số:

  • User : Người nhận thông báo đẩy.
  • payload : Thông tin thông báo, trong đó bao gồm các thông báo headbody .
  • ttl : Thời gian tối đa tính bằng giây mà thông báo sẽ được lưu trữ nếu user offline .

Nếu không có lỗi nào xảy ra, dạng xem trả về một JSONResponse với trạng thái 200 "Thành công" và một đối tượng dữ liệu. Nếu KeyError xảy ra, dạng xem sẽ trả về trạng thái 500 "Lỗi server nội bộ". KeyError xảy ra khi khóa được yêu cầu của một đối tượng không tồn tại.

Trong bước tiếp theo, ta sẽ tạo các tuyến URL tương ứng để phù hợp với các chế độ xem ta đã tạo.

Bước 3 - Ánh xạ URL đến Chế độ xem

Django giúp bạn có thể tạo URL kết nối với các chế độ xem bằng module Python được gọi là URLconf . Mô-đun này ánh xạ các biểu thức đường dẫn URL tới các hàm Python (chế độ xem của bạn). Thông thường, file cấu hình URL được tạo tự động khi bạn tạo một dự án. Trong bước này, bạn sẽ cập nhật file này để bao gồm các tuyến mới cho các chế độ xem mà bạn đã tạo ở bước trước, cùng với các URL cho ứng dụng django-webpush , sẽ cung cấp các điểm cuối để đăng ký user nhận thông báo đẩy.

Để biết thêm thông tin về chế độ xem, vui lòng xem Cách tạo Chế độ xem Django .

Mở urls.py :

  • nano ~/djangopush/djangopush/urls.py

Tệp sẽ trông như thế này:

~ / djangopush / djangopush / urls.py
 """untitled URL Configuration  The `urlpatterns` list routes URLs to views. For more information please see:     https://docs.djangoproject.com/en/2.1/topics/http/urls/ Examples: Function views     1. Add an import:  from my_app import views     2. Add a URL to urlpatterns:  path('', views.home, name='home') Class-based views     1. Add an import:  from other_app.views import Home     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home') Including another URLconf     1. Import the include() function: from django.urls import include, path     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path  urlpatterns = [     path('admin/', admin.site.urls), ] 

Bước tiếp theo là ánh xạ các chế độ xem bạn đã tạo với URL. Đầu tiên, hãy thêm nhập include đảm bảo rằng tất cả các tuyến cho thư viện Django-Webpush sẽ được thêm vào dự án của bạn:

~ / djangopush / djangopush / urls.py
 """webpushdjango URL Configuration ... """ from django.contrib import admin from django.urls import path, include 

Tiếp theo, nhập các chế độ xem bạn đã tạo ở bước cuối cùng và cập nhật danh sách urlpatterns để lập bản đồ các chế độ xem của bạn:

~ / djangopush / djangopush / urls.py
 """webpushdjango URL Configuration ... """ from django.contrib import admin from django.urls import path, include  from .views import home, send_push  urlpatterns = [                   path('admin/', admin.site.urls),                   path('', home),                   path('send_push', send_push),                   path('webpush/', include('webpush.urls')),               ] 

Tại đây, danh sách urlpatterns đăng ký các URL cho gói django-webpush và ánh xạ chế độ xem của bạn tới các URL /send_push/home .

Hãy kiểm tra chế độ xem /home đảm bảo rằng nó đang hoạt động như dự kiến. Đảm bảo rằng bạn đang ở trong folder root của dự án:

  • cd ~/djangopush

Khởi động server của bạn bằng cách chạy lệnh sau:

  • python manage.py runserver your_server_ip:8000

Điều hướng đến http:// your_server_ip :8000 . Bạn sẽ thấy trang chủ sau:

Chế độ xem Trang chủ đầu tiên

Đến đây, bạn có thể tiêu diệt các server với CTRL+C , và ta sẽ chuyển sang tạo mẫu và khiến chúng trong quan điểm của ta bằng cách sử dụng render chức năng.

Bước 4 - Tạo mẫu

Công cụ mẫu của Django cho phép bạn xác định các lớp hướng tới user của ứng dụng của bạn bằng các mẫu tương tự như các file HTML. Trong bước này, bạn sẽ tạo ra và đưa ra một khuôn mẫu để các home xem.

Tạo một folder có tên là templates trong folder root của dự án của bạn:

  • mkdir ~/djangopush/templates

Nếu bạn chạy ls trong folder root của dự án tại thời điểm này, kết quả sẽ giống như sau:

Output
/djangopush /templates db.sqlite3 manage.py /my_env

Tạo một file có tên home.html trong folder templates :

  • nano ~/djangopush/templates/home.html

Thêm mã sau vào file để tạo biểu mẫu nơi user có thể nhập thông tin để tạo thông báo đẩy:

{% load static %} <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <meta http-equiv="X-UA-Compatible" content="ie=edge">     <meta name="vapid-key" content="{{ vapid_key }}">     {% if user.id %}         <meta name="user_id" content="{{ user.id }}">     {% endif %}     <title>Web Push</title>     <link href="https://fonts.googleapis.com/css?family=PT+Sans:400,700" rel="stylesheet"> </head>  <body> <div>     <form id="send-push__form">         <h3 class="header">Send a push notification</h3>         <p class="error"></p>         <input type="text" name="head" placeholder="Header: Your favorite airline 😍">         <textarea name="body" id="" cols="30" rows="10" placeholder="Body: Your flight has been cancelled 😱😱😱"></textarea>         <button>Send Me</button>     </form> </div> </body> </html> 

Các body của file bao gồm một mẫu với hai lĩnh vực: một input yếu tố sẽ tổ chức người đứng đầu / tiêu đề của thông báo và một textarea yếu tố sẽ tổ chức cơ thể thông báo.

Trong phần head của file , có hai thẻ meta sẽ giữ public key VAPID và id của user . Hai biến này được yêu cầu để đăng ký user và gửi thông báo đẩy cho họ. Id của user là bắt buộc ở đây vì bạn sẽ gửi các yêu cầu AJAX đến server và id sẽ được sử dụng để xác định user . Nếu user hiện tại là user đã đăng ký, thì mẫu sẽ tạo một thẻ meta với id của họ là nội dung.

Bước tiếp theo là cho Django biết nơi tìm các mẫu của bạn. Để thực hiện việc này, bạn sẽ chỉnh sửa settings.py và cập nhật danh sách TEMPLATES .

Mở file settings.py :

  • nano ~/djangopush/djangopush/settings.py

Thêm phần sau vào danh sách DIRS để chỉ định đường dẫn đến folder mẫu:

~ / djangopush / djangopush / settings.py
... TEMPLATES = [     {         'BACKEND': 'django.template.backends.django.DjangoTemplates',         'DIRS': [os.path.join(BASE_DIR, 'templates')],         'APP_DIRS': True,         'OPTIONS': {             'context_processors': [                 ...             ],         },     }, ] ... 

Tiếp theo, trong bạn views.py file , cập nhật các home nhằm làm cho home.html mẫu. Mở tập tin:

  • nano ~/djangpush/djangopush/views.py

Đầu tiên, hãy thêm một số nhập bổ sung, bao gồm cấu hình settings , chứa tất cả cài đặt của dự án từ file settings.py và chức năng render từ django.shortcuts :

~ / djangopush / djangopush / views.py
... from django.shortcuts import render, get_object_or_404 ... import json from django.conf import settings  ... 

Tiếp theo, loại bỏ các mã ban đầu bạn đã thêm vào các home xem và thêm những điều sau đây, trong đó quy định cụ thể như thế nào mẫu bạn vừa tạo sẽ được hiển thị:

~ / djangopush / djangopush / views.py
...  @require_GET def home(request):    webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {})    vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY')    user = request.user    return render(request, 'home.html', {user: user, 'vapid_key': vapid_key}) 

Mã chỉ định các biến sau:

  • webpush_settings : Giá trị này được gán cho thuộc tính WEBPUSH_SETTINGS từ cấu hình settings .
  • vapid_key : Điều này nhận giá trị VAPID_PUBLIC_KEY từ đối tượng webpush_settings để gửi cho client . Khóa công khai này được kiểm tra so với private key đảm bảo rằng client có public key được phép nhận thông báo đẩy từ server .
  • user : Biến này đến từ yêu cầu đến. Khi nào user đưa ra yêu cầu đối với server , thông tin chi tiết về user đó sẽ được lưu trữ trong trường user .

Hàm render sẽ trả về một file HTML và một đối tượng ngữ cảnh chứa user hiện tại và public key không hợp lệ của server . Ở đây cần có ba tham số: request , template được hiển thị và đối tượng chứa các biến sẽ được sử dụng trong mẫu.

Với mẫu của ta được tạo và giao diện home được cập nhật, ta có thể chuyển sang cấu hình Django để phục vụ các file tĩnh của ta .

Bước 5 - Cung cấp file tĩnh

Các ứng dụng web bao gồm CSS, JavaScript và các file hình ảnh khác mà Django gọi là “tệp tĩnh”. Django cho phép bạn thu thập tất cả các file tĩnh từ mỗi ứng dụng trong dự án của bạn vào một vị trí duy nhất mà từ đó chúng được phân phát. Giải pháp này được gọi là django.contrib.staticfiles . Trong bước này, ta sẽ cập nhật cài đặt của bạn để cho Django biết nơi các file tĩnh của ta sẽ được lưu trữ.

Mở settings.py :

  • nano ~/djangopush/djangopush/settings.py

Trong settings.py , trước tiên hãy đảm bảo STATIC_URL đã được xác định:

~ / djangopush / djangopush / settings.py
... STATIC_URL = '/static/' 

Tiếp theo, thêm danh sách các folder có tên STATICFILES_DIRS nơi Django sẽ tìm kiếm các file tĩnh:

~ / djangopush / djangopush / settings.py
... STATIC_URL = '/static/' STATICFILES_DIRS = [     os.path.join(BASE_DIR, "static"), ] 

Đến đây bạn có thể thêm STATIC_URL vào danh sách các đường dẫn được xác định trong file urls.py của bạn.

Mở tập tin:

  • nano ~/djangopush/djangopush/urls.py

Thêm mã sau, mã này sẽ nhập cấu hình url static và cập nhật danh sách urlpatterns . Chức năng helper đây sử dụng STATIC_URLSTATIC_ROOT tính ta cung cấp trong settings.py file để phục vụ các file tĩnh của dự án:

~ / djangopush / djangopush / urls.py
 ... from django.conf import settings from django.conf.urls.static import static  urlpatterns = [     ... ]  + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 

Với cài đặt file tĩnh của ta được cấu hình , ta có thể chuyển sang tạo kiểu cho trang chủ của ứng dụng.

Bước 6 - Tạo kiểu cho Trang chủ

Sau khi cài đặt ứng dụng của bạn để phân phát các file tĩnh, bạn có thể tạo một biểu định kiểu bên ngoài và liên kết nó với file home.html để tạo kiểu cho trang chủ. Tất cả các file tĩnh của bạn sẽ được lưu trữ trong một folder static trong folder root của dự án của bạn.

Tạo một folder static và một folder css trong folder static :

  • mkdir -p ~/djangopush/static/css

Mở file css có tên styles.css bên trong folder css :

  • nano ~/djangopush/static/css/styles.css

Thêm các kiểu sau cho trang chủ:

~ / djangopush / static / css / styles.css
 body {     height: 100%;     background: rgba(0, 0, 0, 0.87);     font-family: 'PT Sans', sans-serif; }  div {     height: 100%;     display: flex;     align-items: center;     justify-content: center; }  form {     display: flex;     flex-direction: column;     align-items: center;     justify-content: center;     width: 35%;     margin: 10% auto; }  form > h3 {     font-size: 17px;     font-weight: bold;     margin: 15px 0;     color: orangered;     text-transform: uppercase; }  form > .error {     margin: 0;     font-size: 15px;     font-weight: normal;     color: orange;     opacity: 0.7; }  form > input, form > textarea {     border: 3px solid orangered;     box-shadow: unset;     padding: 13px 12px;     margin: 12px auto;     width: 80%;     font-size: 13px;     font-weight: 500; }  form > input:focus, form > textarea:focus {     border: 3px solid orangered;     box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2);     outline: unset; }  form > button {     justify-self: center;     padding: 12px 25px;     border-radius: 0;     text-transform: uppercase;     font-weight: 600;     background: orangered;     color: white;     border: none;     font-size: 14px;     letter-spacing: -0.1px;     cursor: pointer; }  form > button:disabled {     background: dimgrey;     cursor: not-allowed; } 

Với biểu định kiểu đã tạo, bạn có thể liên kết nó với file home.html bằng các thẻ mẫu tĩnh . Mở file home.html :

  • nano ~/djangopush/templates/home.html

Cập nhật phần head để bao gồm một liên kết đến biểu định kiểu bên ngoài:

~ / djangopush / templates / home.html
 {% load static %} <!DOCTYPE html> <html lang="en">  <head>     ...     <link href="{% static '/css/styles.css' %}" rel="stylesheet"> </head> <body>     ... </body> </html> 

Đảm bảo rằng bạn đang ở trong folder dự án chính của bạn và khởi động lại server để kiểm tra công việc của bạn:

  • cd ~/djangopush
  • python manage.py runserver your_server_ip:8000

Khi bạn truy cập http:// your_server_ip :8000 , nó sẽ giống như sau:

Xem trang chủ
, bạn có thể giết server bằng CTRL+C

Đến đây bạn đã tạo thành công trang home.html và tạo kiểu cho nó, bạn có thể đăng ký user nhận thông báo đẩy khi nào họ truy cập trang chủ.

Bước 7 - Đăng ký nhân viên dịch vụ và đăng ký user đẩy thông báo

Thông báo đẩy trên web có thể thông báo cho user khi có bản cập nhật cho các ứng dụng mà họ đã đăng ký hoặc nhắc họ tương tác lại với các ứng dụng mà họ đã sử dụng trong quá khứ. Chúng dựa trên hai công nghệ, API đẩy và API thông báo . Cả hai công nghệ đều dựa vào sự hiện diện của nhân viên dịch vụ.

Đẩy được gọi khi server cung cấp thông tin cho nhân viên dịch vụ và nhân viên dịch vụ sử dụng API thông báo để hiển thị thông tin này.

Ta sẽ đăng ký user của ta để đẩy và sau đó ta sẽ gửi thông tin từ đăng ký đến server để đăng ký họ.

Trong folder static , tạo một folder có tên js :

  • mkdir ~/djangopush/static/js

Tạo một file có tên registerSw.js :

  • nano ~/djangopush/static/js/registerSw.js

Thêm mã sau để kiểm tra xem nhân viên dịch vụ có được hỗ trợ trên trình duyệt của user hay không trước khi cố gắng đăng ký nhân viên dịch vụ:

~ / djangopush / static / js / registerSw.js
 const registerSw = async () => {     if ('serviceWorker' in navigator) {         const reg = await navigator.serviceWorker.register('sw.js');         initialiseState(reg)      } else {         showNotAllowed("You can't send push notifications ☹️😢")     } }; 

Đầu tiên, hàm registerSw kiểm tra xem trình duyệt có hỗ trợ service worker hay không trước khi đăng ký chúng. Sau khi đăng ký, nó gọi hàm initializeState với dữ liệu đăng ký. Nếu nhân viên dịch vụ không được hỗ trợ trong trình duyệt, nó sẽ gọi hàm showNotAllowed .

Tiếp theo, thêm mã sau vào bên dưới chức năng registerSw để kiểm tra xem user có đủ điều kiện nhận thông báo đẩy hay không trước khi cố gắng đăng ký chúng:

~ / djangopush / static / js / registerSw.js
 ...  const initialiseState = (reg) => {     if (!reg.showNotification) {         showNotAllowed('Showing notifications isn\'t supported ☹️😢');         return     }     if (Notification.permission === 'denied') {         showNotAllowed('You prevented us from showing notifications ☹️🤔');         return     }     if (!'PushManager' in window) {         showNotAllowed("Push isn't allowed in your browser 🤔");         return     }     subscribe(reg); }  const showNotAllowed = (message) => {     const button = document.querySelector('form>button');     button.innerHTML = `${message}`;     button.setAttribute('disabled', 'true'); }; 

Hàm initializeState kiểm tra những điều sau:

  • User có bật thông báo hay không bằng cách sử dụng giá trị của reg.showNotification .
  • User đã cấp quyền cho ứng dụng hiển thị thông báo hay chưa.
  • Trình duyệt có hỗ trợ API PushManager hay không. Nếu bất kỳ kiểm tra nào trong số này không thành công, hàm showNotAllowed sẽ được gọi và đăng ký sẽ bị hủy bỏ.

Chức năng showNotAllowed hiển thị thông báo trên nút và vô hiệu hóa nó nếu user không đủ điều kiện nhận thông báo. Nó cũng hiển thị các thông báo thích hợp nếu user đã hạn chế ứng dụng hiển thị thông báo hoặc nếu trình duyệt không hỗ trợ thông báo đẩy.

Khi ta đảm bảo user đủ điều kiện để nhận thông báo đẩy, bước tiếp theo là đăng ký họ bằng cách sử dụng pushManager . Thêm mã sau vào bên dưới hàm showNotAllowed :

~ / djangopush / static / js / registerSw.js
 ...  function urlB64ToUint8Array(base64String) {     const padding = '='.repeat((4 - base64String.length % 4) % 4);     const base64 = (base64String + padding)         .replace(/\-/g, '+')         .replace(/_/g, '/');      const rawData = window.atob(base64);     const outputArray = new Uint8Array(rawData.length);     const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));      return outputData; }  const subscribe = async (reg) => {     const subscription = await reg.pushManager.getSubscription();     if (subscription) {         sendSubData(subscription);         return;     }      const vapidMeta = document.querySelector('meta[name="vapid-key"]');     const key = vapidMeta.content;     const options = {         userVisibleOnly: true,         // if key exists, create applicationServerKey property         ...(key && {applicationServerKey: urlB64ToUint8Array(key)})     };      const sub = await reg.pushManager.subscribe(options);     sendSubData(sub) }; 

Gọi hàm pushManager.getSubscription trả về dữ liệu cho một đăng ký đang hoạt động. Khi có đăng ký hoạt động, hàm sendSubData được gọi với thông tin đăng ký được chuyển vào dưới dạng tham số.

Khi không tồn tại đăng ký hoạt động, public key VAPID, được mã hóa an toàn cho URL Base64, được chuyển đổi thành Uint8Array bằng cách sử dụng chức năng urlB64ToUint8Array . pushManager.subscribe sau đó được gọi với public key VAPID và giá trị userVisible dưới dạng các tùy chọn. Bạn có thể đọc thêm về các tùy chọn có sẵn tại đây .

Sau khi đăng ký thành công user , bước tiếp theo là gửi dữ liệu đăng ký đến server . Dữ liệu sẽ được gửi đến điểm cuối webpush/save_information được cung cấp bởi gói django-webpush . Thêm mã sau vào bên dưới chức năng subscribe :

~ / djangopush / static / js / registerSw.js
 ...  const sendSubData = async (subscription) => {     const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();     const data = {         status_type: 'subscribe',         subscription: subscription.toJSON(),         browser: browser,     };      const res = await fetch('/webpush/save_information', {         method: 'POST',         body: JSON.stringify(data),         headers: {             'content-type': 'application/json'         },         credentials: "include"     });      handleResponse(res); };  const handleResponse = (res) => {     console.log(res.status); };  registerSw(); 

Điểm cuối save_information yêu cầu thông tin về trạng thái của đăng ký ( subscribeunsubscribe ), dữ liệu đăng ký và trình duyệt. Cuối cùng, ta gọi hàm registerSw() để bắt đầu quá trình đăng ký user .

Tệp đã hoàn thành trông giống như sau:

~ / djangopush / static / js / registerSw.js
 const registerSw = async () => {     if ('serviceWorker' in navigator) {         const reg = await navigator.serviceWorker.register('sw.js');         initialiseState(reg)      } else {         showNotAllowed("You can't send push notifications ☹️😢")     } };  const initialiseState = (reg) => {     if (!reg.showNotification) {         showNotAllowed('Showing notifications isn\'t supported ☹️😢');         return     }     if (Notification.permission === 'denied') {         showNotAllowed('You prevented us from showing notifications ☹️🤔');         return     }     if (!'PushManager' in window) {         showNotAllowed("Push isn't allowed in your browser 🤔");         return     }     subscribe(reg); }  const showNotAllowed = (message) => {     const button = document.querySelector('form>button');     button.innerHTML = `${message}`;     button.setAttribute('disabled', 'true'); };  function urlB64ToUint8Array(base64String) {     const padding = '='.repeat((4 - base64String.length % 4) % 4);     const base64 = (base64String + padding)         .replace(/\-/g, '+')         .replace(/_/g, '/');      const rawData = window.atob(base64);     const outputArray = new Uint8Array(rawData.length);     const outputData = outputArray.map((output, index) => rawData.charCodeAt(index));      return outputData; }  const subscribe = async (reg) => {     const subscription = await reg.pushManager.getSubscription();     if (subscription) {         sendSubData(subscription);         return;     }      const vapidMeta = document.querySelector('meta[name="vapid-key"]');     const key = vapidMeta.content;     const options = {         userVisibleOnly: true,         // if key exists, create applicationServerKey property         ...(key && {applicationServerKey: urlB64ToUint8Array(key)})     };      const sub = await reg.pushManager.subscribe(options);     sendSubData(sub) };  const sendSubData = async (subscription) => {     const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();     const data = {         status_type: 'subscribe',         subscription: subscription.toJSON(),         browser: browser,     };      const res = await fetch('/webpush/save_information', {         method: 'POST',         body: JSON.stringify(data),         headers: {             'content-type': 'application/json'         },         credentials: "include"     });      handleResponse(res); };  const handleResponse = (res) => {     console.log(res.status); };  registerSw(); 

Tiếp theo, thêm một thẻ script cho file registerSw.js trong home.html . Mở tập tin:

  • nano ~/djangopush/templates/home.html

Thêm thẻ script trước thẻ đóng của phần tử body :

~ / djangopush / templates / home.html
 {% load static %} <!DOCTYPE html> <html lang="en">  <head>    ... </head> <body>    ...    <script src="{% static '/js/registerSw.js' %}"></script> </body> </html> 

Bởi vì nhân viên dịch vụ chưa tồn tại, nếu bạn để ứng dụng của bạn đang chạy hoặc cố gắng khởi động lại, bạn sẽ thấy thông báo lỗi. Hãy sửa lỗi này bằng cách tạo một service worker.

Bước 8 - Tạo nhân viên dịch vụ

Để hiển thị thông báo đẩy, bạn cần cài đặt nhân viên dịch vụ đang hoạt động trên trang chủ ứng dụng của bạn . Ta sẽ tạo một nhân viên dịch vụ lắng nghe các sự kiện push và hiển thị thông báo khi sẵn sàng.

Vì ta muốn phạm vi của service worker là toàn bộ domain , ta cần cài đặt nó trong folder root của ứng dụng. Bạn có thể đọc thêm về quy trình trong bài viết này nêu cách đăng ký nhân viên dịch vụ . Cách tiếp cận của ta sẽ là tạo một file sw.js trong folder templates , sau đó ta sẽ đăng ký dưới dạng một chế độ xem.

Tạo file :

  • nano ~/djangopush/templates/sw.js

Thêm mã sau, mã này yêu cầu nhân viên dịch vụ lắng nghe các sự kiện đẩy:

~ / djangopush / Template / sw.js
 // Register event listener for the 'push' event. self.addEventListener('push', function (event) {     // Retrieve the textual payload from event.data (a PushMessageData object).     // Other formats are supported (ArrayBuffer, Blob, JSON), check out the documentation     // on https://developer.mozilla.org/en-US/docs/Web/API/PushMessageData.     const eventInfo = event.data.text();     const data = JSON.parse(eventInfo);     const head = data.head || 'New Notification 🕺🕺';     const body = data.body || 'This is default content. Your notification didn\'t have one 🙄🙄';      // Keep the service worker alive until the notification is created.     event.waitUntil(         self.registration.showNotification(head, {             body: body,             icon: 'https://i.imgur.com/MZM3K5w.png'         })     ); }); 

Nhân viên dịch vụ lắng nghe sự kiện đẩy. Trong hàm gọi lại, dữ liệu event được chuyển đổi thành văn bản. Ta sử dụng mặc định titlebody chuỗi nếu dữ liệu sự kiện không có chúng. Hàm showNotification lấy tiêu đề thông báo, tiêu đề của thông báo được hiển thị và một đối tượng tùy chọn làm tham số. Đối tượng tùy chọn chứa một số thuộc tính để cấu hình các tùy chọn trực quan của thông báo.

Để nhân viên dịch vụ của bạn hoạt động trong toàn bộ domain của bạn, bạn cần cài đặt nó trong folder root của ứng dụng. Ta sẽ sử dụng TemplateView để cho phép nhân viên dịch vụ truy cập vào toàn bộ domain .

Mở file urls.py :

  • nano ~/djangopush/djangopush/urls.py

Thêm một câu lệnh nhập mới và đường dẫn trong danh sách urlpatterns để tạo một dạng xem dựa trên lớp:

~ / djangopush / djangopush / urls.py
... from django.views.generic import TemplateView  urlpatterns = [                   ...,                   path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript'))               ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 

Các chế độ xem dựa trên lớp như TemplateView cho phép bạn tạo các chế độ xem linh hoạt, có thể tái sử dụng. Trong trường hợp này, phương thức TemplateView.as_view tạo một đường dẫn cho service worker bằng cách chuyển service worker vừa tạo làm mẫu và application/x-javascript làm content_type của mẫu.

Đến đây bạn đã tạo một service worker và đăng ký nó dưới dạng một tuyến đường. Tiếp theo, bạn sẽ cài đặt biểu mẫu trên trang chủ để gửi thông báo đẩy.

Bước 9 - Gửi thông báo đẩy

Sử dụng biểu mẫu trên trang chủ, user có thể gửi thông báo đẩy trong khi server của bạn đang chạy. Bạn cũng có thể gửi thông báo đẩy bằng bất kỳ dịch vụ RESTful nào như Postman. Khi user gửi thông báo đẩy từ biểu mẫu trên trang chủ, dữ liệu sẽ bao gồm phần headbody , cũng như id của user nhận. Dữ liệu phải được cấu trúc theo cách sau:

{     head: "Title of the notification",     body: "Notification body",     id: "User's id" } 

Để lắng nghe sự kiện submit của biểu mẫu và gửi dữ liệu do user nhập đến server , ta sẽ tạo một file có tên site.js trong folder ~/djangopush/static/js .

Mở tập tin:

  • nano ~/djangopush/static/js/site.js

Đầu tiên, thêm submit sự kiện submit vào biểu mẫu sẽ cho phép bạn nhận các giá trị của đầu vào biểu mẫu và id user được lưu trữ trong thẻ meta của mẫu của bạn:

~ / djangopush / static / js / site.js
 const pushForm = document.getElementById('send-push__form'); const errorMsg = document.querySelector('.error');  pushForm.addEventListener('submit', async function (e) {     e.preventDefault();     const input = this[0];     const textarea = this[1];     const button = this[2];     errorMsg.innerText = '';      const head = input.value;     const body = textarea.value;     const meta = document.querySelector('meta[name="user_id"]');     const id = meta ? meta.content : null;     ...     // TODO: make an AJAX request to send notification }); 

Hàm pushForm nhận input , vùng textareabutton bên trong biểu mẫu. Nó cũng lấy thông tin từ thẻ meta , bao gồm thuộc tính tên user_id và id của user được lưu trữ trong thuộc tính content của thẻ. Với thông tin này, nó có thể gửi một yêu cầu POST đến điểm cuối /send_push trên server .

Để gửi yêu cầu đến server , ta sẽ sử dụng API tìm nạp root . Ta đang sử dụng Tìm nạp ở đây vì nó được hầu hết các trình duyệt hỗ trợ và không yêu cầu thư viện bên ngoài hoạt động. Bên dưới mã bạn đã thêm, hãy cập nhật hàm pushForm để bao gồm mã gửi yêu cầu AJAX:

~ / djangopush / static / js / site.js
const pushForm = document.getElementById('send-push__form'); const errorMsg = document.querySelector('.error');  pushForm.addEventListener('submit', async function (e) {      ...     const id = meta ? meta.content : null;       if (head && body && id) {         button.innerText = 'Sending...';         button.disabled = true;          const res = await fetch('/send_push', {             method: 'POST',             body: JSON.stringify({head, body, id}),             headers: {                 'content-type': 'application/json'             }         });         if (res.status === 200) {             button.innerText = 'Send another 😃!';             button.disabled = false;             input.value = '';             textarea.value = '';         } else {             errorMsg.innerText = res.message;             button.innerText = 'Something broke 😢..  Try again?';             button.disabled = false;         }     }     else {         let error;         if (!head || !body){             error = 'Please ensure you complete the form 🙏🏾'         }         else if (!id){             error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"         }         errorMsg.innerText = error;     } }); 

Nếu ba tham số bắt buộc head , bodyid có mặt, ta sẽ gửi yêu cầu và tạm thời vô hiệu hóa nút gửi.

Tệp đã hoàn thành trông giống như sau:

~ / djangopush / static / js / site.js
const pushForm = document.getElementById('send-push__form'); const errorMsg = document.querySelector('.error');  pushForm.addEventListener('submit', async function (e) {     e.preventDefault();     const input = this[0];     const textarea = this[1];     const button = this[2];     errorMsg.innerText = '';      const head = input.value;     const body = textarea.value;     const meta = document.querySelector('meta[name="user_id"]');     const id = meta ? meta.content : null;      if (head && body && id) {         button.innerText = 'Sending...';         button.disabled = true;          const res = await fetch('/send_push', {             method: 'POST',             body: JSON.stringify({head, body, id}),             headers: {                 'content-type': 'application/json'             }         });         if (res.status === 200) {             button.innerText = 'Send another 😃!';             button.disabled = false;             input.value = '';             textarea.value = '';         } else {             errorMsg.innerText = res.message;             button.innerText = 'Something broke 😢..  Try again?';             button.disabled = false;         }     }     else {         let error;         if (!head || !body){             error = 'Please ensure you complete the form 🙏🏾'         }         else if (!id){             error = "Are you sure you're logged in? 🤔. Make sure! 👍🏼"         }         errorMsg.innerText = error;     }     }); 

Cuối cùng, thêm site.js file để home.html :

  • nano ~/djangopush/templates/home.html

Thêm thẻ script :

~ / djangopush / templates / home.html
 {% load static %} <!DOCTYPE html> <html lang="en">  <head>    ... </head> <body>    ...    <script src="{% static '/js/site.js' %}"></script> </body> </html> 

Đến đây, nếu bạn để ứng dụng của bạn đang chạy hoặc cố gắng khởi động lại ứng dụng, bạn sẽ thấy lỗi vì nhân viên dịch vụ chỉ có thể hoạt động trong các domain an toàn hoặc trên server localhost . Trong bước tiếp theo, ta sẽ sử dụng ngrok để tạo một tunnel bảo mật đến web server của ta .

Bước 10 - Tạo một tunnel an toàn để kiểm tra ứng dụng

Service worker yêu cầu các kết nối an toàn để hoạt động trên bất kỳ trang nào ngoại trừ localhost vì chúng có thể cho phép các kết nối bị tấn công và phản hồi được lọc và chế tạo. Vì lý do này, ta sẽ tạo một tunnel an toàn cho server của bạn bằng ngrok .

Mở cửa sổ terminal thứ hai và đảm bảo bạn đang ở trong folder chính của bạn :

  • cd ~

Nếu bạn bắt đầu với một server 18.04 sạch trong yêu cầu , thì bạn cần cài đặt unzip :

  • sudo apt update && sudo apt install unzip

Download ngrok:

  • wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
  • unzip ngrok-stable-linux-amd64.zip

Di chuyển ngrok đến /usr/local/bin , để bạn có thể truy cập vào lệnh ngrok từ terminal:

  • sudo mv ngrok /usr/local/bin

Trong cửa sổ terminal đầu tiên của bạn, hãy đảm bảo bạn đang ở trong folder dự án và khởi động server của bạn:

  • cd ~/djangopush
  • python manage.py runserver your_server_ip:8000

Bạn cần làm điều này trước khi tạo tunnel an toàn cho ứng dụng của bạn .

Trong cửa sổ terminal thứ hai của bạn, chuyển đến folder dự án của bạn và kích hoạt môi trường ảo của bạn:

  • cd ~/djangopush
  • source my_env/bin/activate

Tạo tunnel an toàn cho ứng dụng của bạn:

  • ngrok http your_server_ip:8000

Bạn sẽ thấy kết quả sau, bao gồm thông tin về URL ngrok an toàn của bạn:

Output
ngrok by @inconshreveable (Ctrl+C to quit) Session Status online Session Expires 7 hours, 59 minutes Version 2.2.8 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://ngrok_secure_url -> 203.0.113.0:8000 Forwarding https://ngrok_secure_url -> 203.0.113.0:8000 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00

Sao chép ngrok_secure_url từ kết quả của console . Bạn cần thêm nó vào danh sách ALLOWED_HOSTS trong file settings.py của bạn .

Mở một cửa sổ terminal khác, chuyển đến folder dự án của bạn và kích hoạt môi trường ảo của bạn:

  • cd ~/djangopush
  • source my_env/bin/activate

Mở file settings.py :

  • nano ~/djangopush/djangopush/settings.py

Cập nhật danh sách ALLOWED_HOSTS với tunnel bảo mật ngrok:

~ / djangopush / djangopush / settings.py
...  ALLOWED_HOSTS = ['your_server_ip', 'ngrok_secure_url'] ...  

Điều hướng đến trang quản trị bảo mật để đăng nhập: https:// ngrok_secure_url /admin/ . Bạn sẽ thấy một màn hình như sau:

đăng nhập quản trị ngrok

Nhập thông tin admin-user Django của bạn trên màn hình này. Đây phải là thông tin bạn đã nhập khi đăng nhập vào giao diện quản trị trong các bước yêu cầu . Đến đây bạn đã sẵn sàng để gửi thông báo đẩy.

Truy cập https:// ngrok_secure_url trong trình duyệt của bạn. Bạn sẽ thấy dấu nhắc yêu cầu quyền hiển thị thông báo. Nhấp vào nút Cho phép để cho phép trình duyệt của bạn hiển thị thông báo đẩy:

yêu cầu thông báo đẩy

Gửi một biểu mẫu đã điền sẽ hiển thị một thông báo tương tự như sau:

ảnh chụp màn hình thông báo

Lưu ý: Đảm bảo rằng server của bạn đang chạy trước khi cố gắng gửi thông báo.

Nếu bạn nhận được thông báo thì ứng dụng của bạn đang hoạt động như mong đợi.

Bạn đã tạo một ứng dụng web kích hoạt thông báo đẩy trên server và với sự trợ giúp của nhân viên dịch vụ, nhận và hiển thị thông báo. Bạn cũng đã thực hiện các bước để lấy các khóa VAPID được yêu cầu để gửi thông báo đẩy từ server ứng dụng.

Kết luận

Trong hướng dẫn này, bạn đã học cách đăng ký user nhận thông báo đẩy, cài đặt nhân viên dịch vụ và hiển thị thông báo đẩy bằng API thông báo.

Bạn có thể tiến xa hơn nữa bằng cách cấu hình các thông báo để mở các khu vực cụ thể của ứng dụng khi được nhấp vào. Mã nguồn cho hướng dẫn này có thể được tìm thấy ở đây .


Tags:

Các tin liên quan

Cách xây dựng ứng dụng web hiện đại để quản lý thông tin khách hàng với Django và React trên Ubuntu 18.04
2018-10-22
Cách cài đặt Django Web Framework trên Ubuntu 18.04
2018-08-06
Cách sử dụng Trình quản lý cảnh báo và Trình xuất hộp đen để giám sát web server của bạn trên Ubuntu 16.04
2018-05-11
Giải pháp Deep Dive: Xây dựng một ứng dụng web khả dụng cao với khả năng xử lý và lưu trữ web bằng cách sử dụng MongoDB và Elk Stack
2018-03-15
Các bước đầu tiên của bạn với API âm thanh web
2018-01-09
Gói ứng dụng web của bạn bằng bưu kiện
2017-12-09
Tích hợp các thành phần web với ứng dụng Vue.js của bạn
2017-09-25
Cách làm việc với dữ liệu web bằng cách sử dụng yêu cầu và món súp đẹp mắt với Python 3
2017-07-14
Sử dụng Web worker một cách dễ dàng trong Vue.js với vue-worker
2017-05-16
Cách cài đặt Icinga và Icinga Web trên Ubuntu 16.04
2017-05-05