2026年1月11日 星期日

FHIR 03 說明透過 EHR Launch 取得授權碼的程式碼做法與實測過程

 

FHIR 03 說明透過 EHR Launch 取得授權碼的程式碼做法與實測過程

在上一篇文章中 FHIR 02 建立 EHR Launch App ,我們已經成功建立了一個 EHR Launch App,並且在 EHR 系統中註冊完成。 接下來,我們將說明如何透過 EHR Launch 模式來取得授權碼 (Authorization Code),並且展示實際的程式碼做法與測試過程。

若這個系統可以正常運作,那麼將會來了解這個專案內的程式碼作法,並且說明 Launch 這個頁面的運作流程與程式碼設計過程。

執行結果說明

依照底下步驟操作,完成使用 Smart On FHIR 沙盒環境來進行 EHR Launch 的授權碼取得流程:

  • 開啟並且執行 [SmartEHRLaunch1]
  • 這裡是執行後的螢幕截圖,也就是 Blazor 開發框架的範例的啟動畫面 
  • 開啟沙盒驗證網頁 [https://thas.mohw.gov.tw/]
  • 這裡需要先進行身分驗證,點選 [登入] 文字連結
  • 在登入對話窗內,依序輸入帳號、密碼、驗證碼 
  • 最後點選 [登入] 按鈕,完成身分驗證作業
  • 點選上方的 [Sand Box] 連結,進入沙盒系統
  • 現在出現了 [SAND-BOX] 對話窗 
  • 對於 [請選擇] 下拉選單,選擇 [EHR Launch] 項目,也就是預設值
  • 在 [請輸入網址] 欄位中,輸入 https://localhost:7108/launch 
  • 其中, [https://localhost:7108/launch] 端點,就是剛剛撰寫程式碼的 Razor 元件頁面
  • 點選 [完成] 按鈕,系統會導向到 EHR Launch 頁面 
  • 在 [Launch] 端點頁面中,將會顯示出文字 [請稍後,正在初始化中]
  • 此時的網址列將會為 : https://localhost:7108/launch?iss=https://thas.mohw.gov.tw/v/r4/fhir&launch=WzAsIiIsIiIsIkFVVE8iLDAsMCwwLCIiLCIiLCIiLCIiLCIiLCIiLCIiLDAsMSwiIl0
  • 這個網址將會是 Sandbox 系統導向到我們的 EHR Launch 頁面的網址,從這裡也看到了兩個重要的參數:
    • iss :代表 EHR 系統的 FHIR 伺服器位址 https://thas.mohw.gov.tw/v/r4/fhir ,這個將會是我們後續要與之互動的 FHIR 伺服器
    • launch :代表這次啟動的 Launch 參數,也就是 EHR 的系統所產生的 Launch Context
  • 接著,系統會自動將我們的瀏覽器,重新導向到授權伺服器: https://thas.mohw.gov.tw/v/r4/auth/authorize?response_type=code&client_id=smart-app&redirect_uri=https%3A%2F%2Flocalhost%3A7191%2FExchangeToken&scope=openid%20fhirUser%20profile%20launch%2Fpatient%20patient%2F%2A.read%20patient%2FEncounter.read%20patient%2FMedicationRequest.read%20patient%2FServiceRequest.read&state=15fb5f43e861447cbb482a399c4fe7ab&launch=WzAsIiIsIiIsIkFVVE8iLDAsMCwwLCIiLCIiLCIiLCIiLCIiLCIiLCIiLDAsMSwiIl0&aud=https%3A%2F%2Fthas.mohw.gov.tw%2Fv%2Fr4%2Ffhir
  • 此時網頁畫面出會出現底下螢幕截圖,會有這樣的效果,是程式碼會暫停一段時間,故意讓使用者看到準備要切換到該網頁的畫面 
  • 現在,將會進入到 Sand Box 的授權伺服器,要取得授權碼,當取得了授權碼之後,就會使用 redirect_uri 參數,重新導向回到我們開發的頁面端點 https://localhost:7191/ExchangeToken ,並且在網址列中帶入授權碼參數 code
  • 在網頁上,將會看到下面的畫面截圖,準備進入到身分驗證階段,此時的 URL 為: https://thas.mohw.gov.tw/provider-login?response_type=code&client_id=smart-app&redirect_uri=https%3A%2F%2Flocalhost%3A7191%2FExchangeToken&scope=openid+fhirUser+profile+launch%2Fpatient+patient%2F*.read+patient%2FEncounter.read+patient%2FMedicationRequest.read+patient%2FServiceRequest.read&state=ab81aeae4fe84986bcca1e51948fa824&launch=WzAsIiIsIiIsIkFVVE8iLDAsMCwwLCIiLCIiLCIiLCIiLCIiLCIiLCIiLDAsMSwiIl0&aud=https%3A%2F%2Fthas.mohw.gov.tw%2Fv%2Fr4%2Ffhir&login_type=provider 
  • 在這裡使用預設的帳號與密碼,點選 [Login] 按鈕
  • 現在的畫面將會切換到選擇病患的畫面,此時的網址為: https://thas.mohw.gov.tw/select-patient?response_type=code&client_id=smart-app&redirect_uri=https%3A%2F%2Flocalhost%3A7191%2FExchangeToken&scope=openid+fhirUser+profile+launch%2Fpatient+patient%2F*.read+patient%2FEncounter.read+patient%2FMedicationRequest.read+patient%2FServiceRequest.read&state=15fb5f43e861447cbb482a399c4fe7ab&launch=WzAsIiIsIiIsIkFVVE8iLDAsMCwwLCIiLCIiLCIiLCIiLCIiLCIiLCIiLDAsMSwiIl0&aud=https%3A%2F%2Fthas.mohw.gov.tw%2Fv%2Fr4%2Ffhir&login_type=provider&login_success=1&provider=147533 
  • 點選任一病患項目,準備進入到下一個階段
  • 可是,卻看到的底下畫面 
  • 在此看到了 [無法連上這個網站] 的訊息,這是因為我們的 redirect_uri 端點 https://localhost:7191/ExchangeToken?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7Im5lZWRfcGF0aWVudF9iYW5uZXIiOnRydWUsInNtYXJ0X3N0eWxlX3VybCI6Imh0dHBzOi8vdGhhcy5tb2h3Lmdvdi50dy9zbWFydC1zdHlsZS5qc29uIiwicGF0aWVudCI6InBhdGllbnQtVFdFTVItRE1TLTEyMzQ1Njc4OS1BMTIzNDU2Nzg5In0sImNsaWVudF9pZCI6InNtYXJ0LWFwcCIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcxOTEvRXhjaGFuZ2VUb2tlbiIsInNjb3BlIjoib3BlbmlkIGZoaXJVc2VyIHByb2ZpbGUgbGF1bmNoL3BhdGllbnQgcGF0aWVudC8qLnJlYWQgcGF0aWVudC9FbmNvdW50ZXIucmVhZCBwYXRpZW50L01lZGljYXRpb25SZXF1ZXN0LnJlYWQgcGF0aWVudC9TZXJ2aWNlUmVxdWVzdC5yZWFkIiwicGtjZSI6ImF1dG8iLCJjbGllbnRfdHlwZSI6InB1YmxpYyIsInVzZXIiOiJQcmFjdGl0aW9uZXIvMTQ3NTMzIiwiaWF0IjoxNzY4MTI5MjYzLCJleHAiOjE3NjgxMjk1NjN9.yK4RJXRG34pjsdf53PhM6uiZnGOM89w0UdT6-8Wjd_8&state=15fb5f43e861447cbb482a399c4fe7ab
  • 會有這樣的結果,是因為我們上為設計 [ExchangeToken] 頁面程式碼,才會看到這樣的結果導致的
  • 對於 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7Im5lZWRfcGF0aWVudF9iYW5uZXIiOnRydWUsInNtYXJ0X3N0eWxlX3VybCI6Imh0dHBzOi8vdGhhcy5tb2h3Lmdvdi50dy9zbWFydC1zdHlsZS5qc29uIiwicGF0aWVudCI6InBhdGllbnQtVFdFTVItRE1TLTEyMzQ1Njc4OS1BMTIzNDU2Nzg5In0sImNsaWVudF9pZCI6InNtYXJ0LWFwcCIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcxOTEvRXhjaGFuZ2VUb2tlbiIsInNjb3BlIjoib3BlbmlkIGZoaXJVc2VyIHByb2ZpbGUgbGF1bmNoL3BhdGllbnQgcGF0aWVudC8qLnJlYWQgcGF0aWVudC9FbmNvdW50ZXIucmVhZCBwYXRpZW50L01lZGljYXRpb25SZXF1ZXN0LnJlYWQgcGF0aWVudC9TZXJ2aWNlUmVxdWVzdC5yZWFkIiwicGtjZSI6ImF1dG8iLCJjbGllbnRfdHlwZSI6InB1YmxpYyIsInVzZXIiOiJQcmFjdGl0aW9uZXIvMTQ3NTMzIiwiaWF0IjoxNzY4MTI5MjYzLCJleHAiOjE3NjgxMjk1NjN9.yK4RJXRG34pjsdf53PhM6uiZnGOM89w0UdT6-8Wjd_8 則是授權碼
  • 這個授權碼是以 JWT 格式所編碼的字串
  • 將過解譯這個 JWT 字串,將會看到這樣的 JSON 物件
{
  "context": {
    "need_patient_banner": true,
    "smart_style_url": "https://thas.mohw.gov.tw/smart-style.json",
    "patient": "patient-TWEMR-DMS-123456789-A123456789"
  },
  "client_id": "smart-app",
  "redirect_uri": "https://localhost:7191/ExchangeToken",
  "scope": "openid fhirUser profile launch/patient patient/*.read patient/Encounter.read patient/MedicationRequest.read patient/ServiceRequest.read",
  "pkce": "auto",
  "client_type": "public",
  "user": "Practitioner/147533",
  "iat": 1768129263,
  "exp": 1768129563
}
  • 從這裡看到的 [patient] 欄位值 patient-TWEMR-DMS-123456789-A123456789 ,這就是我們這次所選擇的病患資源 ID
  • 到此為止,我們已經成功透過 EHR Launch 模式,取得授權碼,接下來我們將會在下一篇文章中,說明如何使用這個授權碼,來交換取得存取權杖 (Access Token),並且使用這個存取權杖,來存取 FHIR 伺服器中的病患資源資料

專案服務與模型程式碼說明

SettingService.cs

  • 這個服務會注入 IOptions<SettingModel> 物件,來取得 [appsettings.json] 設定檔中的相關設定值
  • 在 [program.cs] 檔案中,已經完成這個服務的注入設定
#region 加入設定強型別注入宣告
builder.Services.Configure<SettingModel>(builder.Configuration
    .GetSection(MagicObjectHelper.SmartAppSettingKey));
#endregion
  • 這個服務內,只有一個方法 GetSettingAsync ,用來取得設定值物件
  • 在 [appsettings.json] 設定檔中,主要是要取得 [RedirectUrl] 這個值,用於當通過授權之後,要重新導向到原有專案內的頁面端點
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "SmartAppSetting": {
    "FhirServerUrl": "https://thas.mohw.gov.tw/v/r4/fhir",
    "RedirectUrl": "https://localhost:7191/ExchangeToken",
    "ClientId": "smart-app"
  }
}

SmartAppSettingService.cs

  • 這個服務將會用於儲存在 Launch 頁面中的狀態值,透過 .NET 提供的 [分散式快取] 機制來存取狀態物件
  • 在 [program.cs] 中,使用底下程式碼註冊了 [分散式快取] 服務
// 提供 IDistributedCache 的記憶體實作
builder.Services.AddDistributedMemoryCache();
  • 在 SmartAppSettingService 服務中,在建構式內注入了 SettingService 物件,因此可以取得 appsetting.json 內的設定值
public SmartAppSettingService(SettingService settingService)
{
    this.settingService = settingService;

    var data = settingService.GetValue();
    Data.FhirServerUrl = data.FhirServerUrl;
    Data.RedirectUrl = data.RedirectUrl;
    Data.ClientId = data.ClientId;
}
  • 在建構式內,這裡將 [FhirServerUrl] 、 [RedirectUrl] 、 [ClientId] 這三個值,設定到 SmartAppSettingModel 物件內,也就是可以用於要儲存到分散式快取的狀態物件內
  • 對於這個方法, UpdateSetting ,則是用來更新分散式快取內的狀態物件
public void UpdateSetting(SmartAppSettingModel model)
{
    Data.FhirServerUrl = model.FhirServerUrl;
    Data.ClientId = model.ClientId;
    Data.RedirectUrl = model.RedirectUrl;
    Data.AuthCode = model.AuthCode;
    Data.ClientState = model.ClientState;
    Data.TokenUrl = model.TokenUrl;
    Data.AuthorizeUrl = model.AuthorizeUrl;
    Data.Iss = model.Iss;
    Data.Launch = model.Launch;
    Data.State = model.State;
}

OAuthStateStoreService.cs

  • 這個物件將會提供 [分散式快取] 的存取功能
  • 在建構式內,注入了 IDistributedCache 物件
public OAuthStateStoreService(IDistributedCache cache) => _cache = cache;
  • 這個服務內,提供了三個方法,分別是 SaveAsync 、 LoadAsync 與 RemoveAsync
  • 這三個方法,分別用來儲存、載入與刪除分散式快取內的狀態物件,這裡使用了 JSON 序列化與反序列化的方式,來將物件轉換成字串,並且存取到分散式快取內

Launch.razor 頁面程式碼說明

  • 在這個頁面,使用了底下語法,來接收使用 Query String 參數傳遞過來的值
[SupplyParameterFromQuery(Name = "iss")]
public string? Iss { get; set; }
[SupplyParameterFromQuery(Name = "launch")]
public string? LaunchCode { get; set; }
  • 在 OnAfterRenderAsync 方法中,這裡會先檢查是否為第一次渲染頁面
  • 在第一次渲染事件發生的時候,將會執行底下的敘述
protected override async System.Threading.Tasks.Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        KeepLaunchIss();
        var bar = await GetMetadataAsync();
        var authUrl = await GetAuthorizeUrlAsync();
        authUrlMessage = $"重新導向到授權伺服器:{authUrl}";

        StateHasChanged();

        await System.Threading.Tasks.Task.Delay(5000);

        NavigationManager.NavigateTo(authUrl);
    }
}
  • 這裡主要會進行:責處理 SMART on FHIR 啟動流程:保存啟動參數、取得 FHIR 伺服器元資料、產生授權 URL 並導向授權伺服器
  • KeepLaunchIss() 方法,保存 SMART on FHIR 啟動參數 (iss 和 launch), 從查詢字串中取得 iss (FHIR 伺服器 URL) 和 launch (啟動代碼) 參數並儲存到應用程式設定中
  • GetMetadataAsync() 方法,從 FHIR 伺服器取得元資料 (CapabilityStatement),解析 SMART on FHIR 的 OAuth 端點 (authorize 和 token URL) 並儲存到應用程式設定中
  • 最後將會,回傳一個布林值,表示是否成功取得授權和令牌端點
  • GetAuthorizeUrlAsync 方法,產生 OAuth 授權 URL,建立 state 參數以防止 CSRF 攻擊,將應用程式設定儲存到狀態存儲中,並組合完整的授權 URL,包含必要的 OAuth 參數:response_type、client_id、redirect_uri、scope、state、launch 和 aud
  • 最後將會,將瀏覽器導向到授權伺服器 URL,開始授權流程

GetMetadataAsync() 方法說明

GetMetadataAsync() 方法主要做三件事:

  • 連接 FHIR 伺服器 - 使用 FhirClient 向 FHIR 伺服器請求 metadata 端點,取得 CapabilityStatement (能力聲明)
  • 解析 OAuth 端點 - 在 CapabilityStatement 中尋找 SMART on FHIR 的 OAuth 擴充資訊 (oauth-uris),提取出:
    • authorize URL (授權端點)
    • token URL (令牌端點)
  • 驗證並回傳結果 - 將這兩個 URL 儲存到 SmartAppSettingService 中,並檢查是否都成功取得。若兩者皆不為空則回傳 true,否則回傳 false
  • 簡單來說,這個方法負責從 FHIR 伺服器探索並取得 OAuth 認證所需的授權和令牌端點 URL。

GetAuthorizeUrlAsync() 方法說明

GetAuthorizeUrlAsync() 方法主要做三件事:

  • 產生安全狀態碼 - 建立一個唯一的 state GUID 字串,用於防止 CSRF (跨站請求偽造) 攻擊
  • 儲存狀態 - 將應用程式的設定資料 (SmartAppSettingModel) 以 state 為鍵值存入 OAuthStateStoreService,保留 10 分鐘有效期限
  • 組合授權 URL - 建構完整的 OAuth 授權請求 URL,包含所有必要參數:
    • response_type=code (授權碼流程)
    • client_id (客戶端識別碼)
    • redirect_uri (回調網址)
    • scope (請求的權限範圍,如讀取病患資料)
    • state (安全狀態碼)
    • launch (SMART 啟動代碼)
    • aud (目標 FHIR 伺服器 URL)
  • 簡單來說,這個方法負責產生並回傳一個完整的 OAuth 授權 URL,用於將使用者重新導向到 FHIR 授權伺服器進行身份驗證。
  • 對於要取得授權碼的 URL,將會使用底下的程式碼
string launchUrl = $"{SmartAppSettingService.Data.AuthorizeUrl}?response_type=code" +
    $"&client_id={SmartAppSettingService.Data.ClientId}" +
    $"&redirect_uri={Uri.EscapeDataString(SmartAppSettingService.Data.RedirectUrl)}" +
    $"&scope={Uri.EscapeDataString("openid fhirUser profile launch/patient patient/*.read patient/Encounter.read patient/MedicationRequest.read patient/ServiceRequest.read")}" +
    $"&state={SmartAppSettingService.Data.State}" +
    $"&launch={SmartAppSettingService.Data.Launch}" +
    $"&aud={Uri.EscapeDataString(SmartAppSettingService.Data.FhirServerUrl)}";
  • 取得到這個 URL 之後,將會使用 NavigationManager.NavigateTo 方法,導向到授權伺服器,進行身分驗證,並且取得授權碼






Github Copilot 11 : 執行時期遇到錯誤的協助的協助

 

Github Copilot 11 : 執行時期遇到錯誤的協助的協助

在這篇文章中所提到的內容,其實是所有程式設計師夢寐以求的技能,也就是在執行階段遇到錯誤的時候,可以有一個強大的助理來幫忙分析與解決問題,在沒有大語言模型 AI 出現的之前,當發生了執行時期的錯誤,通常只能靠著程式設計師自己去分析與解決問題,這個過程通常是非常痛苦的,尤其是當錯誤發生的原因不是很明顯的時候,更是讓人感到挫折與無助;另外就是去尋求高階或者資深的工程師地幫忙來探索與解決此一問題。

這些問題的處理過程所消耗的成本實在巨大,想要解決這些問題,需要具有長時間的經驗累積與工程師的智慧才能做到,但是現在有了大語言模型 AI 的協助,這些問題的處理過程可以大幅度的縮短,甚至可以讓程式設計師在遇到問題的時候,可以直接透過 AI 來協助分析與解決問題,這樣的技術進步,對於程式設計師來說,是一個非常大的福音。

在這裡將會舉出一個實際問題的例子,來說明如何利用 Github Copilot 來協助解決執行時期遇到的錯誤問題。

在這個專案,SmartEHRLaunch1 專案中,這是一個 Smart On FHIR App,而 Smart App 需要能夠支援 OAuth2 授權機制,這個 Smart App 需要透過沙盒的 EHR Launch 模式來啟動,因此,需要透過沙盒來啟動這個應用程式,因此,當這個應用程式網頁後,將會收到來自於 Smart On FHIR 沙盒的兩個參數,分別是 Iss 與 Lauch。

此時這個 Smart App 需要透過這兩個參數,取得 FHIR Server 的相關連線資訊,例如,要取得 OAuth2 授權碼的服務端點,並且使用這個服務端點進行轉向到授權主機上,一旦得到授權碼之後,將會回傳到指定 Redirect Url 頁面,並且將授權碼使用參數方式傳送進來。現在的程式碼因為設計上有問題,因此,在進行授權登入的時候,會需要組成一個授權網址,並且導向到該網址來進行授權登入的動作,這個過程中會需要組成一個授權網址,這個網址的組成過程中,會需要使用到一些參數值,例如 RedirectUrl、FhirServerUrl、AuthorizeUrl 等等。

當修改完成程式之後,進入到執行階段,突然拋出如下例外異常通知,遇到這樣的問題,通常是所有的程式設計師都會感到頭痛與絕望無助的時候,例如,遇到底下的狀況

這裡遇到的 [使用者未處理的例外狀況] 詳細資訊如下

System.ArgumentNullException
  HResult=0x80004003
  Message=Value cannot be null. (Parameter 'stringToEscape')
  Source=System.Private.CoreLib
  StackTrace: 
   於 System.ArgumentNullException.Throw(String paramName)
   於 System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
   於 System.UriHelper.EscapeString(String stringToEscape, Boolean checkExistingEscaped, SearchValues`1 noEscape)
   於 SmartEHRLaunch1.Components.Pages.Launch.<GetAuthorizeUrlAsync>d__18.MoveNext() 在 C:\Vulcan\Projects\SmartEHRLaunch1\SmartEHRLaunch1\Components\Pages\Launch.razor.cs:行 108 中
   於 SmartEHRLaunch1.Components.Pages.Launch.<OnAfterRenderAsync>d__15.MoveNext() 在 C:\Vulcan\Projects\SmartEHRLaunch1\SmartEHRLaunch1\Components\Pages\Launch.razor.cs:行 27 中
   於 Microsoft.AspNetCore.Components.RenderTree.Renderer.<GetErrorHandledTask>d__101.MoveNext()

此時,在這個 [使用者未處理的例外狀況] 對話窗中,會看到 [使用 Copilot 分析] 功能,這是在沒有使用 Github Copilot 功能前所沒有看過的,所以,可以點選該例外狀況對話窗中的 [使用 Copilot 分析] 按鈕,看看會有甚麼結果

現在切換到 [Github Copilot] 視窗中,會看到 Copilot 已經針對這個例外狀況進行了分析,並且提供了解決方案的建議內容

從 Copilot 所提供的建議內容看到 使用 #debugger,分析 'System.ArgumentNullException' 例外狀況 這樣的訊息,表示此時 Copilot 正在抓取 VS 2026 內的相關資訊來進行分析,發現是因為在組成授權網址的時候,某個參數值是 null,因此導致這個例外狀況的發生

錯誤是 System.ArgumentNullException: Value cannot be null. (Parameter 'stringToEscape'),來源是 Uri.EscapeDataString(...) 被傳入了 null。
從除錯資料可知:
•	RedirectUrl 為 null
•	FhirServerUrl 有值
•	AuthorizeUrl 有值
因此在這行發生錯誤:

$"&redirect_uri={Uri.EscapeDataString(SmartAppSettingService.Data.RedirectUrl)}"

從上述的解釋內容,可以知道 RedirectUrl 參數值是 null,因此導致這個例外狀況的發生,現在便可以按照這個線索,來查看為什麼 RedirectUrl 會是 null,接著便可以進行修正,解決這個問題。

現在使用 VS 2026 提供的 [尋找所有參考] 功能,查看這個 RedirectUrl 參數值是從哪裡被設定的。

此時察看到底下程式碼

public SmartAppSettingService(SettingService settingService)
{
    this.settingService = settingService;

    var data = settingService.GetValue();
    Data.FhirServerUrl = data.FhirServerUrl;
    Data.RedirectUrl = data.RedirectUrl;
    Data.ClientId = data.ClientId;
}

最後發現到,這個系統並沒在 appsetting.json 內,定義了 RedirectUrl 這個參數值,因此,導致這個參數值是 null,進而導致在組成授權網址的時候,發生了例外狀況。

只要在 appsetting.json 內,加入 RedirectUrl 這個參數值,並且指定正確的 Redirect Url 網址,便可以解決這個問題。

經過上述的過程,便可以看到,當遇到執行時期的錯誤問題時,並不是程式碼的邏輯上出了問題,而是在於設定檔案內容上出了問題。

現在,將這個問題修正之後,再次執行這個 Smart On FHIR App,便可以順利的進入到授權頁面,並且進行授權登入的動作,整個過程順利完成。

透過 Github Copilot 所提供的 [使用 Copilot 分析] 功能,便可以快速的找出問題的根源,並且進行修正,這樣的功能對於程式設計師來說,是一個非常大的幫助,可以大幅度的提升開發效率,減少在除錯上的時間花費。