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
Section titled “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
Section titled “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.
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
Section titled “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
Section titled “Struktur URL”Rancang struktur URL yang jelas dan hierarkis:
myapp://action/resource?param=value
Contoh:myapp://open/document?id=123myapp://settings/theme?mode=darkmyapp://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
Section titled “Registrasi Platform”Protokol kustom didaftarkan secara berbeda di setiap platform.
Installer NSIS Windows
Section titled “Installer NSIS Windows”Wails v3 secara otomatis mendaftarkan protokol kustom saat menggunakan installer NSIS.
Registrasi Otomatis
Section titled “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
Section titled “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)
Section titled “Registry Manual (Lanjutan)”Jika Anda memerlukan registrasi manual (di luar NSIS):
@echo offREM Register custom protocolREG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp" /ve /d "URL:My Application Protocol" /fREG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp" /v "URL Protocol" /t REG_SZ /d "" /fREG ADD "HKEY_CURRENT_USER\SOFTWARE\Classes\myapp\shell\open\command" /ve /d "\"%1\"" /fPengujian
Section titled “Pengujian”Uji registrasi protokol Anda:
# Buka URL protokol dari PowerShellStart-Process "myapp://test/action"
# Atau dari command promptstart myapp://test/actionPaket MSIX Windows
Section titled “Paket MSIX Windows”Protokol kustom juga secara otomatis didaftarkan saat menggunakan packaging MSIX.
Registrasi Otomatis
Section titled “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)
Section titled “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
Section titled “Konfigurasi Info.plist”Di macOS, protokol didaftarkan melalui file Info.plist Anda.
Konfigurasi Otomatis
Section titled “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
Section titled “Pengujian”# Buka URL protokol dari terminalopen "myapp://test/action"
# Periksa handler yang terdaftar/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | grep myappUniversal Links
Section titled “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
Section titled “Desktop Entry”Di Linux, protokol didaftarkan melalui file .desktop.
Konfigurasi Otomatis
Section titled “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=ApplicationName=My ApplicationExec=/usr/bin/myapp %uMimeType=x-scheme-handler/myapp;Registrasi Manual
Section titled “Registrasi Manual”Jika diperlukan, instal file desktop secara manual:
# Salin file desktopcp myapp.desktop ~/.local/share/applications/
# Perbarui database desktopupdate-desktop-database ~/.local/share/applications/
# Daftarkan protocol handlerxdg-mime default myapp.desktop x-scheme-handler/myappPengujian
Section titled “Pengujian”# Buka URL protokolxdg-open "myapp://test/action"
# Periksa handler yang terdaftarxdg-mime query default x-scheme-handler/myappContoh Lengkap
Section titled “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
Section titled “Integrasi Frontend”Tangani event navigasi di frontend Anda:
import { Events } from '@wailsio/runtime'
// Dengarkan event navigasi dari protocol handlerEvents.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
Section titled “Pertimbangan Keamanan”Validasi Semua Input
Section titled “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
Section titled “Cegah Serangan Injeksi”Jangan pernah mengeksekusi URL secara langsung sebagai kode atau SQL:
// ❌ JANGAN: Eksekusi konten URLfunc badHandler(url string) { exec.Command("sh", "-c", url).Run() // BERBAHAYA!}
// ✅ LAKUKAN: Parse dan validasifunc 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
Section titled “Pengujian”Pengujian Manual
Section titled “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
Section titled “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
Section titled “Pemecahan Masalah”Protokol Tidak Terdaftar
Section titled “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
Section titled “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
Section titled “Praktik Terbaik”✅ Lakukan
Section titled “✅ 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
Section titled “❌ 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
Section titled “Langkah Selanjutnya”- Windows Packaging - Pelajari opsi installer NSIS
- Asosiasi File - Buka file dengan aplikasi Anda
- Single Instance - Cegah beberapa instance aplikasi