Menulis plugin kustom untuk Portal Perangkat Windows

Pelajari cara menulis aplikasi UWP yang menggunakan Windows Device Portal (WDP) untuk menghosting halaman web dan memberikan informasi diagnostik.

Dimulai dengan Pembaruan Pembuat Windows 10 (versi 1703, build 15063), Anda dapat menggunakan Portal Perangkat untuk menghosting antarmuka diagnostik aplikasi Anda. Artikel ini membahas tiga bagian yang diperlukan untuk membuat DevicePortalProvider untuk aplikasi Anda – perubahan manifes paket aplikasi, menyiapkan koneksi aplikasi Anda ke layanan Portal Perangkat, dan menangani permintaan masuk.

Membuat proyek aplikasi UWP baru

Di Microsoft Visual Studio, buat proyek aplikasi UWP baru. Buka File > Proyek Baru > dan pilih Aplikasi Kosong (Windows Universal) untuk C#, lalu klik Berikutnya. Dalam kotak dialog Konfigurasikan proyek baru Anda. Beri nama proyek "DevicePortalProvider" lalu klik Buat. Ini akan menjadi aplikasi yang berisi layanan aplikasi. Anda mungkin perlu memperbarui Visual Studio atau menginstal Windows SDK terbaru.

Menambahkan ekstensi devicePortalProvider ke manifes paket aplikasi Anda

Anda harus menambahkan beberapa kode ke file package.appxmanifest untuk membuat aplikasi Anda berfungsi sebagai plugin Portal Perangkat. Pertama, tambahkan definisi namespace berikut di bagian atas file. Tambahkan juga ke IgnorableNamespaces atribut .

<Package
    ... 
    xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
    xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
    IgnorableNamespaces="uap mp rescap uap4">
    ...

Untuk menyatakan bahwa aplikasi Anda adalah Penyedia Portal Perangkat, Anda perlu membuat layanan aplikasi dan ekstensi Penyedia Portal Perangkat baru yang menggunakannya. Tambahkan ekstensi windows.appService dan ekstensi windows.devicePortalProvider di Extensions elemen di bawah Application. Pastikan AppServiceName atribut cocok di setiap ekstensi. Ini menunjukkan ke layanan Portal Perangkat bahwa layanan aplikasi ini dapat diluncurkan untuk menangani permintaan pada namespace handler.

...   
<Application 
    Id="App" 
    Executable="$targetnametoken$.exe"
    EntryPoint="DevicePortalProvider.App">
    ...
    <Extensions>
        <uap:Extension Category="windows.appService" EntryPoint="MySampleProvider.SampleProvider">
            <uap:AppService Name="com.sampleProvider.wdp" />
        </uap:Extension>
        <uap4:Extension Category="windows.devicePortalProvider">
            <uap4:DevicePortalProvider 
                DisplayName="My Device Portal Provider Sample App" 
                AppServiceName="com.sampleProvider.wdp" 
                HandlerRoute="/MyNamespace/api/" />
        </uap4:Extension>
    </Extensions>
</Application>
...

Atribut mereferensikan HandlerRoute namespace REST yang diklaim oleh aplikasi Anda. Setiap permintaan HTTP pada namespace layanan tersebut (secara implisit diikuti oleh kartubebas) yang diterima oleh layanan Portal Perangkat akan dikirim ke aplikasi Anda untuk ditangani. Dalam hal ini, permintaan <ip_address>/MyNamespace/api/* HTTP yang berhasil diautentikasi akan dikirim ke aplikasi Anda. Konflik antara rute handler diselesaikan melalui pemeriksaan "kemenangan terpanjang": rute mana pun yang cocok dengan lebih banyak permintaan yang dipilih, yang berarti bahwa permintaan ke "/MyNamespace/api/foo" akan cocok dengan penyedia dengan "/MyNamespace/api" daripada satu dengan "/MyNamespace".

Dua kemampuan baru diperlukan untuk fungsionalitas ini. mereka juga harus ditambahkan ke file package.appxmanifest Anda.

...
<Capabilities>
    ...
    <Capability Name="privateNetworkClientServer" />
    <rescap:Capability Name="devicePortalProvider" />
</Capabilities>
...

Catatan

Kemampuan "devicePortalProvider" dibatasi ("rescap"), yang berarti Anda harus mendapatkan persetujuan sebelumnya dari Store sebelum aplikasi Anda dapat diterbitkan di sana. Namun, ini tidak mencegah Anda menguji aplikasi Anda secara lokal melalui sideloading. Untuk informasi selengkapnya tentang kemampuan terbatas, lihat Deklarasi kemampuan aplikasi.

Siapkan tugas latar belakang dan Komponen WinRT Anda

Untuk menyiapkan koneksi Portal Perangkat, aplikasi Anda harus menghubungkan koneksi layanan aplikasi dari layanan Portal Perangkat dengan instans Portal Perangkat yang berjalan dalam aplikasi Anda. Untuk melakukan ini, tambahkan Komponen WinRT baru ke aplikasi Anda dengan kelas yang mengimplementasikan IBackgroundTask.

using Windows.System.Diagnostics.DevicePortal;
using Windows.ApplicationModel.Background;

namespace MySampleProvider {
    // Implementing a DevicePortalConnection in a background task
    public sealed class SampleProvider : IBackgroundTask {
        BackgroundTaskDeferral taskDeferral;
        DevicePortalConnection devicePortalConnection;
        //...
    }

Pastikan namanya cocok dengan namespace layanan dan nama kelas yang disiapkan oleh AppService EntryPoint ("MySampleProvider.SampleProvider"). Saat Anda membuat permintaan pertama ke penyedia Portal Perangkat, Portal Perangkat akan menyimpan permintaan, meluncurkan tugas latar belakang aplikasi Anda, memanggil metode Jalankan, dan meneruskan IBackgroundTaskInstance. Aplikasi Anda kemudian menggunakannya untuk menyiapkan instans DevicePortal Koneksi ion.

// Implement background task handler with a DevicePortalConnection
public void Run(IBackgroundTaskInstance taskInstance) {
    // Take a deferral to allow the background task to continue executing 
    this.taskDeferral = taskInstance.GetDeferral();
    taskInstance.Canceled += TaskInstance_Canceled;

    // Create a DevicePortal client from an AppServiceConnection 
    var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    var appServiceConnection = details.AppServiceConnection;
    this.devicePortalConnection = DevicePortalConnection.GetForAppServiceConnection(appServiceConnection);

    // Add Closed, RequestReceived handlers 
    devicePortalConnection.Closed += DevicePortalConnection_Closed;
    devicePortalConnection.RequestReceived += DevicePortalConnection_RequestReceived;
}

Ada dua peristiwa yang harus ditangani oleh aplikasi untuk menyelesaikan perulangan penanganan permintaan: Ditutup, setiap kali layanan Portal Perangkat dimatikan, dan RequestReceived, yang menampilkan permintaan HTTP masuk dan menyediakan fungsionalitas utama penyedia Portal Perangkat.

Menangani peristiwa RequestReceived

Peristiwa RequestReceived akan dinaikkan sekali untuk setiap permintaan HTTP yang dibuat pada Rute Handler yang ditentukan plugin Anda. Perulangan penanganan permintaan untuk penyedia Portal Perangkat mirip dengan yang ada di NodeJS Express: objek permintaan dan respons disediakan bersama dengan peristiwa, dan handler merespons dengan mengisi objek respons. Di penyedia Portal Perangkat, peristiwa RequestReceived dan handler-nya menggunakan objek Windows.Web.Http.HttpRequestMessage dan HttpResponseMessage.

// Sample RequestReceived echo handler: respond with an HTML page including the query and some additional process information. 
private void DevicePortalConnection_RequestReceived(DevicePortalConnection sender, DevicePortalConnectionRequestReceivedEventArgs args)
{
    var req = args.RequestMessage;
    var res = args.ResponseMessage;

    if (req.RequestUri.AbsolutePath.EndsWith("/echo"))
    {
        // construct an html response message
        string con = "<h1>" + req.RequestUri.AbsoluteUri + "</h1><br/>";
        var proc = Windows.System.Diagnostics.ProcessDiagnosticInfo.GetForCurrentProcess();
        con += String.Format("This process is consuming {0} bytes (Working Set)<br/>", proc.MemoryUsage.GetReport().WorkingSetSizeInBytes);
        con += String.Format("The process PID is {0}<br/>", proc.ProcessId);
        con += String.Format("The executable filename is {0}", proc.ExecutableFileName);
        res.Content = new Windows.Web.HttpStringContent(con);
        res.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("text/html");
        res.StatusCode = Windows.Web.Http.HttpStatusCode.Ok;            
    }
    //...
}

Dalam contoh penanganan permintaan ini, pertama-tama kami menarik objek permintaan dan respons dari parameter args , lalu membuat string dengan URL permintaan dan beberapa pemformatan HTML tambahan. Ini ditambahkan ke dalam objek Respons sebagai instans HttpStringContent. Kelas IHttpContent lainnya, seperti kelas untuk "String" dan "Buffer," juga diizinkan.

Respons kemudian ditetapkan sebagai respons HTTP dan diberi kode status 200 (OK). Ini harus dirender seperti yang diharapkan di browser yang melakukan panggilan asli. Perhatikan bahwa ketika penanganan aktivitas RequestReceived kembali, pesan respons secara otomatis dikembalikan ke agen pengguna: tidak diperlukan metode "kirim" tambahan.

device portal response message

Menyediakan konten statis

Konten statis dapat disajikan langsung dari folder dalam paket Anda, sehingga sangat mudah untuk menambahkan UI ke penyedia Anda. Cara term mudah untuk menyajikan konten statis adalah dengan membuat folder konten di proyek Anda yang dapat memetakan ke URL.

device portal static content folder

Kemudian, tambahkan handler rute di penanganan aktivitas RequestReceived yang mendeteksi rute konten statis dan memetakan permintaan dengan tepat.

if (req.RequestUri.LocalPath.ToLower().Contains("/www/")) {
    var filePath = req.RequestUri.AbsolutePath.Replace('/', '\\').ToLower();
    filePath = filePath.Replace("\\backgroundprovider", "")
    try {
        var fileStream = Windows.ApplicationModel.Package.Current.InstalledLocation.OpenStreamForReadAsync(filePath).GetAwaiter().GetResult();
        res.StatusCode = HttpStatusCode.Ok;
        res.Content = new HttpStreamContent(fileStream.AsInputStream());
        res.Content.Headers.ContentType = new HttpMediaTypeHeaderValue("text/html");
    } catch(FileNotFoundException e) {
        string con = String.Format("<h1>{0} - not found</h1>\r\n", filePath);
        con += "Exception: " + e.ToString();
        res.Content = new Windows.Web.Http.HttpStringContent(con);
        res.StatusCode = Windows.Web.Http.HttpStatusCode.NotFound;
        res.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("text/html");
    }
}

Pastikan bahwa semua file di dalam folder konten ditandai sebagai "Konten" dan diatur ke "Salin jika lebih baru" atau "Salin selalu" di menu Properti Visual Studio. Ini memastikan bahwa file akan berada di dalam Paket AppX Anda saat Anda menyebarkannya.

configure static content file copying

Menggunakan sumber daya dan API Portal Perangkat yang ada

Konten statis yang dilayani oleh penyedia Portal Perangkat dilayani pada port yang sama dengan layanan Portal Perangkat inti. Ini berarti Anda dapat mereferensikan JS dan CSS yang ada yang disertakan dengan Portal Perangkat dengan tag dan <script> sederhana <link> di HTML Anda. Secara umum, kami menyarankan penggunaan rest.js, yang membungkus semua REST API Portal Perangkat inti dalam objek webbRest yang nyaman, dan file common.css , yang akan memungkinkan Anda untuk menata konten Anda agar sesuai dengan UI Portal Perangkat lainnya. Anda dapat melihat contoh ini di halaman index.html yang disertakan dalam sampel. Ini menggunakan rest.js untuk mengambil nama perangkat dan menjalankan proses dari Portal Perangkat.

device portal plugin output

Yang penting, penggunaan metode HttpPost/DeleteExpect200 di webbRest akan secara otomatis melakukan penanganan CSRF untuk Anda, yang memungkinkan halaman web Anda memanggil REST API yang mengubah status.

Catatan

Konten statis yang disertakan dengan Portal Perangkat tidak dilengkapi dengan jaminan terhadap perubahan yang melanggar. Meskipun API tidak diharapkan sering berubah, api mungkin, terutama dalam file common.js dan controls.js , yang tidak boleh digunakan penyedia Anda.

Men-debug koneksi Portal Perangkat

Untuk men-debug tugas latar belakang, Anda harus mengubah cara Visual Studio menjalankan kode Anda. Ikuti langkah-langkah di bawah ini untuk menelusuri kesalahan koneksi layanan aplikasi untuk memeriksa bagaimana penyedia Anda menangani permintaan HTTP:

  1. Dari menu Debug, pilih DevicePortalProvider Properties.
  2. Di bawah tab Penelusuran Kesalahan, di bagian Mulai tindakan, pilih "Jangan luncurkan, tetapi debug kode saya saat dimulai".
    put plugin in debug mode
  3. Atur titik henti dalam fungsi handler RequestReceived Anda. break point at requestreceived handler

Catatan

Pastikan arsitektur build cocok dengan arsitektur target dengan tepat. Jika Anda menggunakan PC 64-bit, Anda harus menyebarkan menggunakan build AMD64. 4. Tekan F5 untuk menyebarkan aplikasi Anda 5. Nonaktifkan Portal Perangkat, lalu nyalakan kembali sehingga menemukan aplikasi Anda (hanya diperlukan saat Anda mengubah manifes aplikasi - sisa waktu yang Anda lakukan cukup menyebarkan ulang dan melewati langkah ini). 6. Di browser Anda, akses namespace penyedia, dan titik henti harus dipukul.