สร้างอินเทอร์เฟซการค้นหาด้วยวิดเจ็ตการค้นหา

วิดเจ็ตการค้นหามีอินเทอร์เฟซการค้นหาที่ปรับแต่งได้สำหรับเว็บแอปพลิเคชัน วิดเจ็ตนี้กำหนดให้ใช้ HTML และ JavaScript เพียงเล็กน้อยและเปิดใช้ฟีเจอร์การค้นหาทั่วไป เช่น Facet และการใส่เลขหน้า คุณยังปรับแต่งส่วนต่างๆ ของอินเทอร์เฟซได้ด้วย CSS และ JavaScript

หากคุณต้องการความยืดหยุ่นมากกว่าที่วิดเจ็ตมีให้ ให้ลองใช้ API การค้นหา สำหรับข้อมูลเกี่ยวกับการสร้างอินเทอร์เฟซการค้นหาด้วย API การค้นหา โปรดดูที่การสร้างอินเทอร์เฟซการค้นหาด้วย API การค้นหา

สร้างอินเทอร์เฟซการค้นหา

การสร้างอินเทอร์เฟซการค้นหามีหลายขั้นตอนดังนี้

  1. กำหนดค่าแอปพลิเคชันการค้นหา
  2. สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน
  3. เพิ่มมาร์กอัป HTML สำหรับช่องค้นหาและผลลัพธ์
  4. โหลดวิดเจ็ตบนหน้าเว็บ
  5. เริ่มต้นวิดเจ็ต

กำหนดค่าแอปพลิเคชันการค้นหา

อินเทอร์เฟซการค้นหาแต่ละรายการต้องมีแอปพลิเคชันการค้นหาที่กำหนดไว้ในคอนโซลผู้ดูแลระบบ แอปพลิเคชันการค้นหาให้ข้อมูลเพิ่มเติมสำหรับคำค้นหา เช่น แหล่งข้อมูล ข้อมูลประกอบ และการตั้งค่าคุณภาพการค้นหา

หากต้องการสร้างแอปพลิเคชันการค้นหา โปรดดูที่หัวข้อสร้างประสบการณ์การค้นหาที่กําหนดเอง

สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน

นอกเหนือจากขั้นตอนในกำหนดค่าการเข้าถึง Google Cloud Search API แล้ว คุณต้องสร้างรหัสไคลเอ็นต์สำหรับเว็บแอปพลิเคชันด้วย

กำหนดค่าโปรเจ็กต์

สิ่งที่จะเกิดขึ้นเมื่อกำหนดค่าโปรเจ็กต์มีดังนี้

  • เลือกประเภทไคลเอ็นต์เว็บเบราว์เซอร์
  • ระบุ URL ต้นทาง ของแอป
  • บันทึกรหัสไคลเอ็นต์ที่สร้างขึ้น คุณจะต้องใช้รหัสไคลเอ็นต์เพื่อ ดำเนินขั้นตอนถัดไปให้เสร็จสมบูรณ์ ทั้งนี้ ไม่จำเป็นต้องมีรหัสลับไคลเอ็นต์ สำหรับวิดเจ็ต

โปรดดูข้อมูลเพิ่มเติมที่ OAuth 2.0 สำหรับเว็บแอปพลิเคชันฝั่งไคลเอ็นต์

เพิ่มมาร์กอัป HTML

วิดเจ็ตนี้กำหนดให้ใช้ HTML เพียงเล็กน้อยจึงจะทำงานได้ คุณต้องระบุข้อมูลต่อไปนี้

  • องค์ประกอบ input สำหรับช่องค้นหา
  • องค์ประกอบที่จะตรึงป๊อปอัปคำแนะนำไว้
  • องค์ประกอบที่จะรวมผลการค้นหา
  • (ไม่บังคับ) ระบุองค์ประกอบเพื่อใส่ตัวควบคุมประกอบ

ข้อมูลโค้ด HTML ต่อไปนี้แสดง HTML สำหรับวิดเจ็ต Search โดยแอตทริบิวต์ id จะระบุองค์ประกอบที่จะเชื่อมโยง

serving/widget/public/with_css/index.html
<div id="search_bar">
  <div id="suggestions_anchor">
    <input type="text" id="search_input" placeholder="Search for...">
  </div>
</div>
<div id="facet_results"></div>
<div id="search_results"></div>

โหลดวิดเจ็ต

วิดเจ็ตจะโหลดแบบไดนามิกผ่านสคริปต์ตัวโหลด หากต้องการรวมตัวโหลด ให้ใช้แท็ก <script> ตามที่แสดง

serving/widget/public/with_css/index.html
<!-- Google API loader -->
<script src="https://apis.google.com/js/api.js?mods=enable_cloud_search_widget&onload=onLoad" async defer></script>

คุณต้องระบุ Callback onload ในแท็กสคริปต์ ระบบจะเรียกฟังก์ชันนี้เมื่อ ตัวโหลดพร้อมใช้งาน เมื่อตัวโหลดพร้อมแล้ว ให้โหลดวิดเจ็ตต่อไปโดยเรียกใช้ gapi.load() เพื่อโหลดโมดูลไคลเอ็นต์ API, Google Sign-In และ Cloud Search

serving/widget/public/with_css/app.js
/**
* Load the cloud search widget & auth libraries. Runs after
* the initial gapi bootstrap library is ready.
*/
function onLoad() {
  gapi.load('client:auth2:cloudsearch-widget', initializeApp)
}

ระบบจะเรียกใช้ฟังก์ชัน initializeApp() หลังจากโหลดโมดูลทั้งหมดแล้ว

เริ่มต้นวิดเจ็ต

ก่อนอื่น ให้เริ่มต้นไลบรารีของไคลเอ็นต์โดยการเรียกใช้ gapi.client.init() หรือ gapi.auth2.init() ด้วยรหัสไคลเอ็นต์ที่สร้างขึ้นและขอบเขต https://www.googleapis.com/auth/cloud_search.query จากนั้นใช้คลาส gapi.cloudsearch.widget.resultscontainer.Builder และ gapi.cloudsearch.widget.searchbox.Builder เพื่อกำหนดค่าวิดเจ็ตและเชื่อมโยงกับองค์ประกอบ HTML

ตัวอย่างต่อไปนี้แสดงวิธีเริ่มใช้งานวิดเจ็ต

serving/widget/public/with_css/app.js
/**
 * Initialize the app after loading the Google API client &
 * Cloud Search widget.
 */
function initializeApp() {
  // Load client ID & search app.
  loadConfiguration().then(function() {
    // Set API version to v1.
    gapi.config.update('cloudsearch.config/apiVersion', 'v1');

    // Build the result container and bind to DOM elements.
    var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setSearchResultsContainerElement(document.getElementById('search_results'))
      .setFacetResultsContainerElement(document.getElementById('facet_results'))
      .build();

    // Build the search box and bind to DOM elements.
    var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setInput(document.getElementById('search_input'))
      .setAnchor(document.getElementById('suggestions_anchor'))
      .setResultsContainer(resultsContainer)
      .build();
  }).then(function() {
    // Init API/oauth client w/client ID.
    return gapi.auth2.init({
        'clientId': clientId,
        'scope': 'https://www.googleapis.com/auth/cloud_search.query'
    });
  });
}

ตัวอย่างข้างต้นอ้างอิงถึงตัวแปร 2 ตัวสำหรับการกำหนดค่าที่กำหนดไว้ ดังนี้

serving/widget/public/with_css/app.js
/**
* Client ID from OAuth credentials.
*/
var clientId = "...apps.googleusercontent.com";

/**
* Full resource name of the search application, such as
* "searchapplications/<your-id>".
*/
var searchApplicationName = "searchapplications/...";

กำหนดค่าประสบการณ์ในการลงชื่อเข้าใช้

โดยค่าเริ่มต้น วิดเจ็ตจะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้และให้สิทธิ์แอปเมื่อเริ่มพิมพ์คำค้นหา คุณใช้ Google Sign-In สําหรับเว็บไซต์เพื่อมอบประสบการณ์การลงชื่อเข้าใช้ที่ปรับให้เหมาะกับผู้ใช้แต่ละคนมากขึ้นได้

ให้สิทธิ์ผู้ใช้โดยตรง

ใช้ลงชื่อเข้าใช้ด้วย Google เพื่อตรวจสอบสถานะการลงชื่อเข้าใช้ของผู้ใช้และผู้ใช้ที่ลงชื่อเข้าใช้หรือออกจากระบบตามที่จำเป็น ตัวอย่างเช่น ตัวอย่างต่อไปนี้จะสังเกตสถานะ isSignedIn เพื่อตรวจสอบการเปลี่ยนแปลงการลงชื่อเข้าใช้และใช้เมธอด GoogleAuth.signIn() เพื่อเริ่มการลงชื่อเข้าใช้จากการคลิกปุ่ม

serving/widget/public/with_signin/app.js
// Handle sign-in/sign-out.
let auth = gapi.auth2.getAuthInstance();

// Watch for sign in status changes to update the UI appropriately.
let onSignInChanged = (isSignedIn) => {
  // Update UI to switch between signed in/out states
  // ...
}
auth.isSignedIn.listen(onSignInChanged);
onSignInChanged(auth.isSignedIn.get()); // Trigger with current status.

// Connect sign-in/sign-out buttons.
document.getElementById("sign-in").onclick = function(e) {
  auth.signIn();
};
document.getElementById("sign-out").onclick = function(e) {
  auth.signOut();
};

โปรดดูรายละเอียดเพิ่มเติมที่หัวข้อลงชื่อเข้าใช้ด้วย Google

ลงชื่อเข้าใช้ให้ผู้ใช้โดยอัตโนมัติ

คุณสามารถปรับปรุงประสบการณ์การลงชื่อเข้าใช้ให้ดีขึ้นได้โดยให้สิทธิ์แอปพลิเคชันล่วงหน้าในนามของผู้ใช้ในองค์กร เทคนิคนี้ยังมีประโยชน์หากใช้พร็อกซี Cloud Identity Aware เพื่อป้องกันแอปพลิเคชัน

โปรดดูข้อมูลเพิ่มเติมที่หัวข้อใช้ Google Sign-In กับแอปไอที

ปรับแต่งอินเทอร์เฟซ

คุณสามารถเปลี่ยนลักษณะที่ปรากฏของอินเทอร์เฟซการค้นหาได้โดยใช้เทคนิคต่างๆ ต่อไปนี้

  • ลบล้างรูปแบบด้วย CSS
  • ตกแต่งองค์ประกอบด้วยอะแดปเตอร์
  • สร้างองค์ประกอบที่กำหนดเองด้วยอะแดปเตอร์

ลบล้างรูปแบบด้วย CSS

วิดเจ็ต Search มาพร้อมกับ CSS ของตัวเองเพื่อให้คำแนะนำรูปแบบและเอลิเมนต์ผลลัพธ์ รวมถึงส่วนควบคุมการใส่เลขหน้า คุณสามารถจัดรูปแบบองค์ประกอบเหล่านี้ใหม่ได้ตามต้องการ

ในระหว่างการโหลด วิดเจ็ต Search จะโหลดสไตล์ชีตเริ่มต้นแบบไดนามิก ซึ่งจะเกิดขึ้นหลังจากโหลดสไตล์ชีตของแอปพลิเคชันแล้ว ซึ่งเป็นการเพิ่มลำดับความสำคัญของกฎ เพื่อให้สไตล์ของคุณมีลำดับความสำคัญเหนือรูปแบบเริ่มต้น ให้ใช้ตัวเลือกระดับบนเพื่อเพิ่มความเฉพาะเจาะจงของกฎเริ่มต้น

เช่น กฎต่อไปนี้จะไม่มีผลหากโหลดในแท็ก link แบบคงที่หรือ style ในเอกสาร

.cloudsearch_suggestion_container {
  font-size: 14px;
}

แต่ให้ปรับแต่งกฎด้วยรหัสหรือคลาสของคอนเทนเนอร์ระดับบนที่ประกาศไว้ในหน้าเว็บแทน

#suggestions_anchor .cloudsearch_suggestion_container {
  font-size: 14px;
}

ดูรายการคลาสการสนับสนุนและตัวอย่าง HTML ที่สร้างโดยวิดเจ็ตได้ที่ข้อมูลอ้างอิงคลาส CSS ที่รองรับ

ตกแต่งองค์ประกอบด้วยอะแดปเตอร์

ในการตกแต่งองค์ประกอบก่อนการแสดงภาพ ให้สร้างและเสียบอะแดปเตอร์ที่ใช้วิธีการตกแต่งแบบใดแบบหนึ่ง เช่น decorateSuggestionElement หรือ decorateSearchResultElement.

เช่น อะแดปเตอร์ต่อไปนี้เพิ่มคลาสที่กำหนดเองลงในองค์ประกอบคำแนะนำและผลลัพธ์

serving/widget/public/with_decorated_element/app.js
/**
 * Search box adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.decorateSuggestionElement = function(element) {
  element.classList.add('my-suggestion');
}

/**
 * Results container adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.decorateSearchResultElement = function(element) {
  element.classList.add('my-result');
}

หากต้องการลงทะเบียนอะแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้เมธอด setAdapter() ของคลาส Builder ที่เกี่ยวข้อง ดังนี้

serving/widget/public/with_decorated_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

เติมแต่งอาจแก้ไขแอตทริบิวต์ขององค์ประกอบคอนเทนเนอร์ ตลอดจนองค์ประกอบย่อยอื่นๆ คุณอาจเพิ่มหรือนำองค์ประกอบย่อยออกระหว่างการตกแต่งได้ อย่างไรก็ตาม หากทำการเปลี่ยนแปลงเชิงโครงสร้างองค์ประกอบ ให้ลองสร้างองค์ประกอบโดยตรงแทนการตกแต่ง

สร้างองค์ประกอบที่กำหนดเองด้วยอะแดปเตอร์

หากต้องการสร้างองค์ประกอบที่กำหนดเองสำหรับคำแนะนำ คอนเทนเนอร์ข้อมูลประกอบ หรือผลการค้นหา ให้สร้างและลงทะเบียนอะแดปเตอร์ที่ใช้งาน createSuggestionElement, createFacetResultElement หรือ createSearchResultElement ซ้ำ

อะแดปเตอร์ต่อไปนี้แสดงการสร้างองค์ประกอบคำแนะนำที่กำหนดเองและผลการค้นหาโดยใช้แท็ก HTML <template>

serving/widget/public/with_custom_element/app.js
/**
 * Search box adapter that overrides creation of suggestion elements.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.createSuggestionElement = function(suggestion) {
  let template = document.querySelector('#suggestion_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.suggested_query').textContent = suggestion.suggestedQuery;
  return fragment.firstElementChild;
}

/**
 * Results container adapter that overrides creation of result elements.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.createSearchResultElement = function(result) {
  let template = document.querySelector('#result_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.title').textContent = result.title;
  fragment.querySelector('.title').href = result.url;
  let snippetText = result.snippet != null ?
    result.snippet.snippet : '';
  fragment.querySelector('.query_snippet').innerHTML = snippetText;
  return fragment.firstElementChild;
}

หากต้องการลงทะเบียนอะแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้เมธอด setAdapter() ของคลาส Builder ที่เกี่ยวข้อง ดังนี้

serving/widget/public/with_custom_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

การสร้างองค์ประกอบข้อมูลประกอบที่กำหนดเองด้วย createFacetResultElement มีข้อจำกัดหลายประการดังนี้

  • คุณต้องแนบคลาส CSS cloudsearch_facet_bucket_clickable เข้ากับองค์ประกอบที่ผู้ใช้คลิกเพื่อสลับที่เก็บข้อมูล
  • คุณต้องรวมที่เก็บข้อมูลแต่ละชุดในองค์ประกอบที่มีอยู่ด้วย cloudsearch_facet_bucket_container คลาส CSS
  • คุณแสดงผลที่เก็บข้อมูลในลำดับที่ต่างจากที่ปรากฏในคำตอบไม่ได้

เช่น ข้อมูลโค้ดต่อไปนี้แสดงผลข้อมูลประกอบโดยใช้ลิงก์แทนช่องทำเครื่องหมาย

serving/widget/public/with_custom_facet/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}

ResultsContainerAdapter.prototype.createFacetResultElement = function(result) {
  // container for the facet
  var container = document.createElement('div');

  // Add a label describing the facet (operator/property)
  var label = document.createElement('div')
  label.classList.add('facet_label');
  label.textContent = result.operatorName;
  container.appendChild(label);

  // Add each bucket
  for(var i in result.buckets) {
    var bucket = document.createElement('div');
    bucket.classList.add('cloudsearch_facet_bucket_container');

    // Extract & render value from structured value
    // Note: implementation of renderValue() not shown
    var bucketValue = this.renderValue(result.buckets[i].value)
    var link = document.createElement('a');
    link.classList.add('cloudsearch_facet_bucket_clickable');
    link.textContent = bucketValue;
    bucket.appendChild(link);
    container.appendChild(bucket);
  }
  return container;
}

// Renders a value for user display
ResultsContainerAdapter.prototype.renderValue = function(value) {
  // ...
}

ปรับแต่งพฤติกรรมการค้นหา

การตั้งค่าแอปพลิเคชันการค้นหาแสดงการกำหนดค่าเริ่มต้นสำหรับอินเทอร์เฟซการค้นหาและเป็นค่าคงที่ หากต้องการใช้ตัวกรองหรือข้อมูลประกอบแบบไดนามิก เช่น การอนุญาตให้ผู้ใช้เปิด/ปิดแหล่งข้อมูล คุณสามารถลบล้างการตั้งค่าแอปพลิเคชันการค้นหาได้โดยการสกัดกั้นคำขอค้นหาด้วยอะแดปเตอร์

ใช้อะแดปเตอร์กับเมธอด interceptSearchRequest เพื่อแก้ไขคำขอที่ส่งไปยัง Search API ก่อนดำเนินการ

เช่น อะแดปเตอร์ต่อไปนี้จะสกัดกั้นคำขอจำกัดการค้นหาให้อยู่ในแหล่งที่มาที่ผู้ใช้เลือกเท่านั้น

serving/widget/public/with_request_interceptor/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}
ResultsContainerAdapter.prototype.interceptSearchRequest = function(request) {
  if (!this.selectedSource || this.selectedSource == 'ALL') {
    // Everything selected, fall back to sources defined in the search
    // application.
    request.dataSourceRestrictions = null;
  } else {
    // Restrict to a single selected source.
    request.dataSourceRestrictions = [
      {
        source: {
          predefinedSource: this.selectedSource
        }
      }
    ];
  }
  return request;
}

หากต้องการลงทะเบียนอะแดปเตอร์เมื่อเริ่มต้นวิดเจ็ต ให้ใช้เมธอด setAdapter() เมื่อสร้าง ResultsContainer

serving/widget/public/with_request_interceptor/app.js
var resultsContainerAdapter = new ResultsContainerAdapter();
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(resultsContainerAdapter)
  // ...
  .build();

HTML ต่อไปนี้ใช้เพื่อแสดงกล่องสำหรับกรองตามแหล่งที่มา

serving/widget/public/with_request_interceptor/index.html
<div>
  <span>Source</span>
  <select id="sources">
    <option value="ALL">All</option>
    <option value="GOOGLE_GMAIL">Gmail</option>
    <option value="GOOGLE_DRIVE">Drive</option>
    <option value="GOOGLE_SITES">Sites</option>
    <option value="GOOGLE_GROUPS">Groups</option>
    <option value="GOOGLE_CALENDAR">Calendar</option>
    <option value="GOOGLE_KEEP">Keep</option>
  </select>
</div>

โค้ดต่อไปนี้จะรับข้อมูลการเปลี่ยนแปลง ตั้งค่าการเลือก และเรียกใช้การค้นหาอีกครั้งหากจำเป็น

serving/widget/public/with_request_interceptor/app.js
// Handle source selection
document.getElementById('sources').onchange = (e) => {
  resultsContainerAdapter.selectedSource = e.target.value;
  let request = resultsContainer.getCurrentRequest();
  if (request.query) {
    // Re-execute if there's a valid query. The source selection
    // will be applied in the interceptor.
    resultsContainer.resetState();
    resultsContainer.executeRequest(request);
  }
}

นอกจากนี้ คุณยังสกัดกั้นการค้นหาคำตอบได้โดยใช้ interceptSearchResponse ในอะแดปเตอร์

ปักหมุดเวอร์ชัน API

โดยค่าเริ่มต้น วิดเจ็ตจะใช้ API เวอร์ชันเสถียรล่าสุด หากต้องการล็อกเฉพาะเวอร์ชันใดเวอร์ชันหนึ่ง ให้ตั้งค่าพารามิเตอร์การกำหนดค่า cloudsearch.config/apiVersion เป็นเวอร์ชันที่ต้องการก่อนที่จะเริ่มต้นวิดเจ็ต

serving/widget/public/basic/app.js
gapi.config.update('cloudsearch.config/apiVersion', 'v1');

เวอร์ชัน API จะมีค่าเริ่มต้นเป็น 1.0 หากไม่ได้ตั้งค่าหรือตั้งเป็นค่าที่ไม่ถูกต้อง

ปักหมุดเวอร์ชันวิดเจ็ต

เพื่อหลีกเลี่ยงการเปลี่ยนแปลงที่ไม่คาดคิดในอินเทอร์เฟซการค้นหา ให้ตั้งค่าพารามิเตอร์การกำหนดค่า cloudsearch.config/clientVersion ตามที่แสดงไว้ต่อไปนี้

gapi.config.update('cloudsearch.config/clientVersion', 1.1);

เวอร์ชันวิดเจ็ตจะมีค่าเริ่มต้นเป็น 1.0 หากไม่ได้ตั้งค่าหรือตั้งเป็นค่าที่ไม่ถูกต้อง

รักษาความปลอดภัยให้อินเทอร์เฟซการค้นหา

ผลการค้นหามีข้อมูลที่ละเอียดอ่อนมาก ทำตามแนวทางปฏิบัติที่ดีที่สุดเพื่อรักษาความปลอดภัยให้กับเว็บแอปพลิเคชัน โดยเฉพาะการป้องกันการโจมตีจากการหลอกให้คลิก

ดูข้อมูลเพิ่มเติมได้ที่โปรเจ็กต์คู่มือ OWASP

เปิดใช้การแก้ไขข้อบกพร่อง

ใช้ interceptSearchRequest เพื่อเปิดการแก้ไขข้อบกพร่องสำหรับวิดเจ็ต Search เช่น

  if (!request.requestOptions) {
  // Make sure requestOptions is populated
  request.requestOptions = {};
  }
  // Enable debugging
  request.requestOptions.debugOptions = {enableDebugging: true}

  return request;