Protokol URL Kustom
Protokol URL kustom (juga disebut skema URL) memungkinkan aplikasi Anda diluncurkan saat pengguna mengklik tautan dengan protokol kustom Anda, seperti myapp://action atau myapp://open/document.
Ringkasan
Protokol kustom memungkinkan:
- Deep linking: Meluncurkan aplikasi Anda dengan data spesifik
- Integrasi browser: Menangani tautan dari halaman web
- Tautan email: Membuka aplikasi Anda dari klien email
- Komunikasi antar aplikasi: Meluncurkan dari aplikasi lain
Contoh: myapp://open/document?id=123 meluncurkan aplikasi Anda dan membuka dokumen 123.
Konfigurasi
Definisikan protokol kustom di opsi aplikasi Anda:
Protokol kustom dideklarasikan di build/config.yml (yang dikonsumsi oleh platform packager — macro NSIS di Windows, manifest MSIX, CFBundleURLTypes macOS, .desktop/xdg-mime Linux — saat waktu package). Tidak ada tipe application.Protocol dan tidak ada field Protocols pada application.Options.
# build/config.yml
protocols:
- scheme: myapp
description: "My Application Protocol"Di kode Go, dengarkan peluncuran dengan URL melalui event ApplicationLaunchedWithUrl:
package main
import (
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
func main() {
app := application.New(application.Options{
Name: "My Application",
Description: "My awesome application",
})
app.Event.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
handleCustomURL(e.Context().URL())
})
app.Run()
}
func handleCustomURL(url string) {
// Parse dan tangani URL kustom
// Contoh: myapp://open/document?id=123
println("Received URL:", url)
}Protocol Handler
Dengarkan event protokol untuk menangani URL masuk:
app.Event.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
url := e.Context().URL()
// Parse URL
parsedURL, err := parseCustomURL(url)
if err != nil {
app.Logger.Error("Failed to parse URL:", err)
return
}
// Tangani aksi berbeda
switch parsedURL.Action {
case "open":
openDocument(parsedURL.DocumentID)
case "settings":
showSettings()
case "user":
showUser Profile(parsedURL.UserID)
default:
app.Logger.Warn("Unknown action:", parsedURL.Action)
}
})Struktur URL
Rancang struktur URL yang jelas dan hierarkis:
myapp://action/resource?param=value
Contoh:
myapp://open/document?id=123
myapp://settings/theme?mode=dark
myapp://user/profile?username=johnPraktik terbaik:
- Gunakan nama skema huruf kecil
- Buat skema pendek dan mudah diingat
- Gunakan path hierarkis untuk resource
- Sertakan parameter query untuk data opsional
- Encode karakter khusus dalam URL
Registrasi Platform
Protokol kustom didaftarkan secara berbeda di setiap platform.
Installer NSIS Windows
Wails v3 secara otomatis mendaftarkan protokol kustom saat menggunakan installer NSIS.
Registrasi Otomatis
Saat Anda membangun aplikasi dengan wails3 build, installer NSIS:
- Secara otomatis mendaftarkan semua protokol yang dideklarasikan di
build/config.ymldi bawah kunciprotocols: - Mengasosiasikan protokol dengan executable aplikasi Anda
- Menyiapkan entri registry yang benar
- Menghapus asosiasi protokol saat uninstall
Tidak diperlukan konfigurasi tambahan!
Cara Kerjanya
Template NSIS menyertakan macro bawaan:
wails.associateCustomProtocols- Mendaftarkan protokol saat instalasiwails.unassociateCustomProtocols- Menghapus protokol saat uninstall
Macro ini secara otomatis dipanggil berdasarkan konfigurasi Protocols Anda.
Registry Manual (Lanjutan)
Jika Anda memerlukan registrasi manual (di luar NSIS):
@echo off
REM Register custom protocol
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp" /ve /d "URL:My Application Protocol" /f
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp" /v "URL Protocol" /t REG_SZ /d "" /f
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp\shell\open\command" /ve /d "\"%1\"" /fPengujian
Uji registrasi protokol Anda:
# Buka URL protokol dari PowerShell
Start-Process "myapp://test/action"
# Atau dari command prompt
start myapp://test/action
Paket MSIX Windows
Protokol kustom juga secara otomatis didaftarkan saat menggunakan packaging MSIX.
Registrasi Otomatis
Saat Anda membangun aplikasi dengan MSIX, manifest secara otomatis menyertakan registrasi protokol dari konfigurasi protokol build/config.yml Anda.
Manifest yang di-generate menyertakan:
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="myapp">
<uap:DisplayName>My Application Protocol</uap:DisplayName>
</uap:Protocol>
</uap:Extension>Universal Links (Web-to-App Linking)
Windows mendukung Web-to-App linking, yang bekerja serupa dengan Universal Links di macOS. Saat mendeploy aplikasi Anda sebagai paket MSIX, Anda dapat mengaktifkan tautan HTTPS untuk meluncurkan aplikasi Anda secara langsung.
Untuk mengaktifkan Web-to-App linking, ikuti panduan Microsoft tentang web-to-app linking. Anda perlu:
- Tambahkan App URI Handler secara manual ke manifest MSIX Anda (
build/windows/msix/app_manifest.xml):
<uap3:Extension Category="windows.appUriHandler">
<uap3:AppUriHandler>
<uap3:Host Name="myawesomeapp.com"/>
</uap3:AppUriHandler>
</uap3:Extension>- Konfigurasi
windows-app-web-linkdi website Anda: Host filewindows-app-web-linkdihttps://myawesomeapp.com/.well-known/windows-app-web-link. File ini harus berisi informasi paket aplikasi Anda dan path yang ditangani.
Saat tautan Web-to-App meluncurkan aplikasi Anda, Anda akan menerima event ApplicationLaunchedWithUrl yang sama seperti dengan skema protokol kustom.
Konfigurasi Info.plist
Di macOS, protokol didaftarkan melalui file Info.plist Anda.
Konfigurasi Otomatis
Wails secara otomatis menghasilkan Info.plist dengan protokol Anda saat Anda build dengan wails3 build.
Protokol yang dideklarasikan di build/config.yml ditambahkan ke:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>My Application Protocol</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>Pengujian
# Buka URL protokol dari terminal
open "myapp://test/action"
# Periksa handler yang terdaftar
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | grep myapp
Universal Links
Selain skema protokol kustom, macOS juga mendukung Universal Links, yang memungkinkan aplikasi Anda diluncurkan oleh tautan HTTPS biasa (mis. https://myawesomeapp.com/path). Universal Links memberikan pengalaman pengguna yang mulus antara aplikasi web dan desktop Anda.
Untuk mengaktifkan Universal Links, ikuti panduan Apple tentang mendukung Universal Links di aplikasi Anda. Anda perlu:
- Tambahkan entitlements di
entitlements.plistAnda:
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:myawesomeapp.com</string>
</array>- Tambahkan NSUserActivityTypes ke Info.plist:
<key>NSUserActivityTypes</key>
<array>
<string>NSUserActivityTypeBrowsingWeb</string>
</array>- Konfigurasi
apple-app-site-associationdi website Anda: Host fileapple-app-site-associationdihttps://myawesomeapp.com/.well-known/apple-app-site-association.
Saat Universal Link memicu aplikasi Anda, Anda akan menerima event ApplicationLaunchedWithUrl yang sama, sehingga kode penanganannya identik dengan skema protokol kustom.
Desktop Entry
Di Linux, protokol didaftarkan melalui file .desktop.
Konfigurasi Otomatis
Wails menghasilkan file desktop entry dengan protocol handler saat Anda build dengan wails3 build.
Diperbaiki di v3: Template desktop Linux sekarang secara benar menyertakan penanganan protokol.
File desktop yang di-generate menyertakan:
[Desktop Entry]
Type=Application
Name=My Application
Exec=/usr/bin/myapp %u
MimeType=x-scheme-handler/myapp;Registrasi Manual
Jika diperlukan, instal file desktop secara manual:
# Salin file desktop
cp myapp.desktop ~/.local/share/applications/
# Perbarui database desktop
update-desktop-database ~/.local/share/applications/
# Daftarkan protocol handler
xdg-mime default myapp.desktop x-scheme-handler/myapp
Pengujian
# Buka URL protokol
xdg-open "myapp://test/action"
# Periksa handler yang terdaftar
xdg-mime query default x-scheme-handler/myapp
Contoh Lengkap
Berikut contoh lengkap yang menangani beberapa aksi protokol:
package main
import (
"fmt"
"net/url"
"strings"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
type App struct {
app *application.App
window *application.WebviewWindow
}
func main() {
// Registrasi protokol ada di build/config.yml (platform packager
// mengonsumsinya); kode aplikasi hanya mendengarkan event peluncuran.
app := application.New(application.Options{
Name: "DeepLink Demo",
Description: "Custom protocol demonstration",
})
myApp := &App{app: app}
myApp.setup()
app.Run()
}
func (a *App) setup() {
// Buat window
a.window = a.app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "DeepLink Demo",
Width: 800,
Height: 600,
URL: "http://wails.localhost/",
})
// Tangani URL protokol kustom
a.app.Event.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
a.handleDeepLink(e.Context().URL())
})
}
func (a *App) handleDeepLink(rawURL string) {
// Parse URL
parsedURL, err := url.Parse(rawURL)
if err != nil {
a.app.Logger.Error("Failed to parse URL:", err)
return
}
// Bawa window ke depan
a.window.Show()
a.window.Focus()
// Ekstrak path dan query
path := strings.Trim(parsedURL.Path, "/")
query := parsedURL.Query()
// Tangani aksi berbeda
parts := strings.Split(path, "/")
if len(parts) == 0 {
return
}
action := parts[0]
switch action {
case "open":
if len(parts) >= 2 {
resource := parts[1]
id := query.Get("id")
a.openResource(resource, id)
}
case "settings":
section := ""
if len(parts) >= 2 {
section = parts[1]
}
a.openSettings(section)
case "user":
if len(parts) >= 2 {
username := parts[1]
a.openUserProfile(username)
}
default:
a.app.Logger.Warn("Unknown action:", action)
}
}
func (a *App) openResource(resourceType, id string) {
fmt.Printf("Opening %s with ID: %s\n", resourceType, id)
// Emit event ke frontend
a.app.Event.Emit("navigate", map[string]string{
"type": resourceType,
"id": id,
})
}
func (a *App) openSettings(section string) {
fmt.Printf("Opening settings section: %s\n", section)
a.app.Event.Emit("navigate", map[string]string{
"page": "settings",
"section": section,
})
}
func (a *App) openUserProfile(username string) {
fmt.Printf("Opening user profile: %s\n", username)
a.app.Event.Emit("navigate", map[string]string{
"page": "user",
"user": username,
})
}Integrasi Frontend
Tangani event navigasi di frontend Anda:
import { Events } from '@wailsio/runtime'
// Dengarkan event navigasi dari protocol handler
Events.On('navigate', (event) => {
const { type, id, page, section, user } = event.data
if (type === 'document') {
// Buka dokumen dengan ID
router.push(`/document/${id}`)
} else if (page === 'settings') {
// Buka pengaturan
router.push(`/settings/${section}`)
} else if (page === 'user') {
// Buka profil pengguna
router.push(`/user/${user}`)
}
})Pertimbangan Keamanan
Validasi Semua Input
Selalu validasi dan sanitasi URL dari sumber eksternal:
func (a *App) handleDeepLink(rawURL string) {
// Parse URL
parsedURL, err := url.Parse(rawURL)
if err != nil {
a.app.Logger.Error("Invalid URL:", err)
return
}
// Validasi skema
if parsedURL.Scheme != "myapp" {
a.app.Logger.Warn("Invalid scheme:", parsedURL.Scheme)
return
}
// Validasi path
path := strings.Trim(parsedURL.Path, "/")
if !isValidPath(path) {
a.app.Logger.Warn("Invalid path:", path)
return
}
// Sanitasi parameter
params := sanitizeQueryParams(parsedURL.Query())
// Proses URL yang sudah divalidasi
a.processDeepLink(path, params)
}
func isValidPath(path string) bool {
// Hanya izinkan alfanumerik dan forward slash
validPath := regexp.MustCompile(`^[a-zA-Z0-9/]+$`)
return validPath.MatchString(path)
}
func sanitizeQueryParams(query url.Values) map[string]string {
sanitized := make(map[string]string)
for key, values := range query {
if len(values) > 0 {
// Ambil nilai pertama dan sanitasi
sanitized[key] = sanitizeString(values[0])
}
}
return sanitized
}Cegah Serangan Injeksi
Jangan pernah mengeksekusi URL secara langsung sebagai kode atau SQL:
// ❌ JANGAN: Eksekusi konten URL
func badHandler(url string) {
exec.Command("sh", "-c", url).Run() // BERBAHAYA!
}
// ✅ LAKUKAN: Parse dan validasi
func goodHandler(url string) {
parsed, _ := url.Parse(url)
action := parsed.Query().Get("action")
// Whitelist aksi yang diizinkan
allowed := map[string]bool{
"open": true,
"settings": true,
"help": true,
}
if allowed[action] {
handleAction(action)
}
}Pengujian
Pengujian Manual
Uji protocol handler selama pengembangan:
Windows:
Start-Process "myapp://test/action?id=123"
macOS:
open "myapp://test/action?id=123"
Linux:
xdg-open "myapp://test/action?id=123"
Pengujian HTML
Buat halaman HTML untuk pengujian:
<!DOCTYPE html>
<html>
<head>
<title>Protocol Test</title>
</head>
<body>
<h1>Custom Protocol Test Links</h1>
<ul>
<li><a href="myapp://open/document?id=123">Open Document 123</a></li>
<li><a href="myapp://settings/theme?mode=dark">Dark Mode Settings</a></li>
<li><a href="myapp://user/profile?username=john">User Profile</a></li>
</ul>
</body>
</html>Pemecahan Masalah
Protokol Tidak Terdaftar
Windows:
- Periksa registry:
HKEY_CURRENT_USER\SOFTWARE\Classes\<scheme> - Instal ulang dengan installer NSIS
- Verifikasi installer dijalankan dengan izin yang benar
macOS:
- Build ulang aplikasi dengan
wails3 build - Periksa
Info.plistdi app bundle:MyApp.app/Contents/Info.plist - Reset Launch Services:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill
Linux:
- Periksa file desktop:
~/.local/share/applications/myapp.desktop - Perbarui database:
update-desktop-database ~/.local/share/applications/ - Verifikasi handler:
xdg-mime query default x-scheme-handler/myapp
Aplikasi Tidak Meluncur
Periksa log:
app := application.New(application.Options{
LogLevel: slog.LevelDebug, // requires `import "log/slog"`
// ...
})Masalah umum:
- Aplikasi tidak terinstal di lokasi yang diharapkan
- Path executable di registrasi tidak cocok dengan lokasi aktual
- Masalah izin
Praktik Terbaik
✅ Lakukan
- Gunakan nama skema yang deskriptif -
mycompany-myappalih-alihmca - Validasi semua input - Jangan pernah percaya URL dari sumber eksternal
- Tangani error dengan baik - Log URL yang tidak valid, jangan crash
- Berikan umpan balik ke pengguna - Tampilkan aksi apa yang dipicu
- Uji di semua platform - Penanganan protokol bervariasi
- Dokumentasikan struktur URL Anda - Bantu pengguna dan integrator
❌ Jangan
- Jangan gunakan nama skema umum - Hindari
http,file,app, dll. - Jangan eksekusi URL sebagai kode - Risiko keamanan besar
- Jangan ekspos operasi sensitif - Wajibkan konfirmasi untuk aksi destruktif
- Jangan asumsikan protokol bekerja di mana-mana - Siapkan mekanisme fallback
- Jangan lupa URL encoding - Tangani karakter khusus dengan benar
Langkah Selanjutnya
- Windows Packaging - Pelajari opsi installer NSIS
- Asosiasi File - Buka file dengan aplikasi Anda
- Single Instance - Cegah beberapa instance aplikasi