JavaScript >> Javascript 文檔 >  >> Node.js

如何使用 Node.js 編寫 DNS 檢查器

如何使用 Node.js DNS 包對域執行 DNS 查找並構建簡單的 UI 以自動執行查找過程。

開始使用

在本教程中,我們將使用 CheatCode 的全棧 JavaScript 框架 Joystick。 Joystick 將前端 UI 框架與用於構建應用的 Node.js 後端結合在一起。

首先,我們要通過 NPM 安裝 Joystick。確保在安裝之前使用 Node.js 16+ 以確保兼容性(如果您需要學習如何安裝 Node.js 或在計算機上運行多個版本,請先閱讀本教程):

終端

npm i -g @joystick.js/cli

這將在您的計算機上全局安裝操縱桿。安裝好之後,接下來我們新建一個項目:

終端

joystick create app

幾秒鐘後,您將看到一條消息已註銷到 cd 進入你的新項目並運行 joystick start

終端

cd app && joystick start

在此之後,您的應用應該可以運行了,我們可以開始了。

為檢索 DNS 記錄連接 getter

首先,我們將使用 Joystick 的 getter 來連接我們的 DNS 記錄的檢索。在 /api 運行 joystick create 時創建的文件夾 (在項目的根目錄),我們要創建一個新文件夾 dns 在其中,一個名為 getters.js 的文件 :

/api/dns/getters.js

import DNS from 'dns';

const dns = DNS.promises;

export default {
  checkDNS: {
    input: {
      domain: {
        type: "string",
        required: true,
      },
    },
    get: async (input = {}) => {
      return {
        ipAddresses: await dns.resolve(input?.domain, 'A').catch((error) => {
          if (error?.code === 'ENODATA') {
            return [];
          }
        }),
        cname: await dns.resolve(input?.domain, 'CNAME').catch((error) => {
          if (error?.code === 'ENODATA') {
            return [];
          }
        }),
        nameserver: await dns.resolveNs(input?.domain).catch((error) => {
          if (error?.code === 'ENODATA') {
            return [];
          }
        }),
      }
    },
  },
};

因為我們需要寫的代碼比較簡單,所以上面我們輸出了我們需要寫的完整文件。

首先,在文件的頂部,注意我們已經導入了 DNS 來自 dns 包裹。這裡,dns 是一個內置的 Node.js 包,用於在 Node.js 應用程序中處理 DNS 記錄。我們在這里為導入的值使用了全大寫版本,因為我們想使用包方法的 JavaScript Promise 版本——而不是默認的回調/異步版本。

要訪問它,我們在 import const dns 下方創建一個新變量 存儲值 DNS.promises (包存儲其基於 Promise 的 API)。

向下移動,從我們的文件中導出一個純 JavaScript 對象,並在其上添加一個屬性 checkDNS 設置為另一個對象。這裡,checkDNSgetter 的名稱 我們要定義。請注意,我們在要導出的父對像上定義它,這意味著,如果我們願意,我們可以在一個文件中定義多個 getter(我們將看到如何使用它接下來)。

專注於設置為 checkDNS 的值 , 關於那個 對象,我們有兩個屬性:

  1. input 它描述了我們預期傳遞給 getter 的輸入值的預期形狀。
  2. get 這是通過檢索和返回一些數據來處理或“解決”getter 請求的函數。

對於 input ,為了檢索 DNS 信息,我們需要一個域名(在 dns 的文檔中 包這可互換地稱為“主機名”)。到 input ,我們傳遞一個對象,描述我們期望隨請求接收的輸入對象的形狀。在這裡,我們期望一個屬性 domain 我們想要驗證它是否包含一個 JavaScript 數據類型為 string 的值 並且該值存在(建議通過設置 requiredtrue 這裡)。

一旦我們的輸入通過驗證,接下來,我們需要連接 get() 實際響應的功能 向我們的 getter 請求。

在該函數內部,作為第一個參數,我們採用經過驗證的 input 我們從客戶那裡收到(這與客戶最初傳遞給我們的內容沒有修改)。

在內部,我們設置代碼以返回一個對象,該對象將描述我們關心的域的不同 DNS 記錄,特別是:ipAddresses , cname , 和 nameserver .

為了檢索每個,我們把 dns 包使用。注意前面傳遞給get的函數 ,我們添加了關鍵字 async .這告訴 JavaScript 我們將使用 await 關鍵字被添加到該關鍵字的函數內部,以便“等待”我們將其放在前面的函數的響應。

如我們所見,我們對 dns.<method> 的每次調用 正在使用 await 關鍵詞。這意味著我們希望這些函數返回一個我們想要等待響應的 JavaScript Promise。我們使用 dns 中的兩個不同函數 這裡:

  1. dns.resolve() 它將主機名作為第一個參數,將 DNS 記錄類型作為第二個參數。這會將找到的該記錄類型的值作為數組返回。
  2. dns.resolveNs() 它將主機名作為其第一個參數並返回與域關聯的 DNS 名稱服務器數組。

要檢索我們域的任何已知 IP 地址,我們調用 dns.resolve() 傳遞 A DNS 記錄類型。要檢索我們域名的任何已知 cname,我們傳遞 CNAME DNS 記錄類型。

最後,要檢索我們域的任何已知名稱服務器,我們調用 dns.resolveNs() 傳遞我們的域名。

對於所有三個電話,我們想提請注意兩件事。首先,對於我們的主機名值,我們傳遞 input.domain 這是我們期望從對我們的 getter 的請求中收到的域。接下來,在每個函數調用的末尾,我們添加了一個 .catch() 回調表示“如果此函數沒有收到任何相關數據或有錯誤,執行此操作 ." 這裡,"this" 是檢查 error.code 值設置為 ENODATA 如果無法檢索給定的 DNS 記錄,這是我們期望的響應。

如果不能,我們希望返回一個空數組(這樣可以避免破壞 getter 並表示無法找到該值的數據)。

而已!接下來,我們需要將這個 getter 實際連接到我們的 API 以確保它是可訪問的。

/api/index.js

import dnsGetters from './dns/getters';

export default {
  getters: {
    ...dnsGetters,
  },
  setters: {},
};

這裡,在 /api/index.js 內部 (運行joystick create時自動為我們生成的文件 ) 我們已經導入了從 /api/dns/getters.js 導出的對象 作為 dnsGetters .下面,在我們從 this 導出的對像上 文件,我們有兩個屬性:getterssetters 設置為自己的對象。在這裡,我們定義了所有的 getter 和 setter(getter 的兄弟姐妹,幫助我們在應用程序中“設置”或修改數據)。

我們在這裡看到的模式純粹是組織性的。為了保持我們的代碼整潔,我們把我們的 dnsGetters 在另一個文件中,然後使用 ... JavaScript 中的擴展運算符將它們“解包”到全局 getters 對像在這裡。我們說“全局”是因為我們在此處定義的任何內容都會在 /index.server.js 中傳遞給操縱桿 作為 api 價值。 Joystick 使用它為我們的每個 getter 和 setter 生成 HTTP 端點。

如果我們繼續保存這個文件,為了證明這一點,如果我們現在打開瀏覽器並運行以下命令,我們應該會得到響應:

http://localhost:2600/api/_getters/checkDNS?input={%22domain%22:%22cheatcode.co%22}

請注意,在這裡,我們的 getter 在 /api/_getters/checkDNS 處通過操縱桿自動註冊為我們的應用程序中的 HTTP 端點 .

接下來,作為結束,我們將連接一個 UI 組件,為我們提供一個簡單的表單,用於調用我們的 getter 並在瀏覽器中顯示響應。

為我們的 UI 連接路由

在我們快速移動到客戶端之前,我們想為我們將要構建的頁面連接一條路由並創建一個虛擬組件。

/index.server.js

import node from "@joystick.js/node";
import api from "./api";

node.app({
  api,
  routes: {
    "/": (req, res) => { ... },
    "/dns": (req, res) => {
      res.render("ui/pages/dns/index.js");
    },
    "*": (req, res) => { ... },
  },
});

在這裡,在我們的 index.server.js 文件(這是負責啟動我們的服務器的文件,Joystick 通過 joystick start 自動為我們運行 ),到 routes 對象,我們添加了一個新路由 /dns .在幕後,Joystick 會自動將其註冊為 Express.js 路由(這是 Joystick 在內部使用來運行我們的 HTTP 服務器),獲取我們在此處傳遞的函數並將其用作路由的“處理程序”。

如果你以前用過 Express.js,這相當於寫了類似的東西......

app.get('/dns', (req, res) => {
  res.render('ui/pages/dns/index.js');
});

這裡唯一的區別是,Joystick 為我們提供了一種定義路線的標準化方法,然後自動為我們生成此代碼。此外,在 res Express.js 傳遞給我們的對象,Joystick 定義了一個特殊的 .render() 該函數旨在在我們傳遞它的路徑處渲染 Joystick 組件。

在這裡,我們期待在 /ui/pages/dns/index.js 處表示我們應用中的頁面的操縱桿組件 .現在讓我們為其添加一個佔位符:

/ui/pages/dns/index.js

import ui from '@joystick.js/ui';

const DNS = ui.component({
  render: () => {
    return `
      <div>
        <p>DNS</p>
      </div>
    `;
  },
});

export default DNS;

在該文件中,我們導入 ui 來自 @joystick.js/ui 這是操縱桿框架的“前端”部分。在 ui 我們在這裡導入的對象,方法 component() 被定義,這有助於我們定義一個 Joystick 組件。

在那個組件上,我們定義了一個 render 函數返回我們想要表示我們的組件的 HTML 標記字符串。在這裡,首先,我們只是添加一個普通的 <div></div> 帶有 <p></p> 的標籤 裡面的標籤。

有了這個和 index.server.js 以上保存,如果我們訪問 http://localhost:2600/dns 在我們的瀏覽器中,我們應該會在屏幕上看到“DNS”文本。

連接用於檢索 DNS 記錄的 UI

回到我們剛剛添加的組件骨架,現在,我們想要擴展它以包含一個用於輸入域的表單,一種顯示我們對 checkDNS 調用結果的方式 ,以及通過我們的 UI 調用我們的 getter 的所有必要修飾。

/ui/pages/dns/index.js

import ui, { get } from '@joystick.js/ui';

const DNS = ui.component({
  state: {
    dns: null,
  },
  events: {
    'submit form': (event, component) => {
      event.preventDefault();

      get('checkDNS', {
        input: {
          domain: event?.target?.domain?.value,
        },
      }).then((response) => {
        component.setState({ dns: response });
      }).catch((error) => {
        console.warn(error);
      });
    },
  },
  css: `
    table {
      width: 50%;
    }

    table tr th,
    table tr td {
      border: 1px solid #eee;
      padding: 10px;
    }

    table tr th {
      text-align: left;
    }
  `,
  render: ({ state, when }) => {
    return `
      <div>
        <form>
          <input type="text" name="domain" placeholder="Type your domain name here..." />
          <button type="submit">Check DNS</button>
        </form>
        ${when(!!state.dns, `
          <table>
            <tbody>
              <tr>
                <th>IP Addresses</th>
                <td>${state.dns?.ipAddresses?.join(', ')}</td>
              </tr>
              <tr>
                <th>CNAMEs</th>
                <td>${state.dns?.cname?.join(', ')}</td>
              </tr>
              <tr>
                <th>Nameservers</th>
                <td>${state.dns?.nameserver?.join(', ')}</td>
              </tr>
            </tbody>
          </table>
        `)}
      </div>
    `;
  },
});

export default DNS;

這就是我們需要的一切。讓我們來看看它。首先,在我們組件的頂部,我們添加了一個屬性 state 設置為包含屬性 dns 的對象 設置為 null .正如我們將看到的,這是我們期望從 getter 接收到的數據存儲在我們的組件中的地方。在這裡,我們設置該值 dnsnull 作為一個默認 價值。

接下來,跳轉到 render 函數,使用 JavaScript 解構從作為第一個參數傳遞給我們的 render 的組件實例中“提取”值 函數,我們可以訪問 state (我們剛剛設置的默認值)和 when ,一個“渲染函數”(操縱桿組件中的一種特殊類型的函數),它允許我們有條件地在我們的組件中渲染一些 HTML when 滿足一些條件。

如果我們查看從 render 返回的 HTML 字符串 函數,我們可以看到一個表達式${} 正在使用(這在 JavaScript 中稱為“插值”,允許我們在 JavaScript 字符串中傳遞動態值),調用 when() 從裡面經過。到那個when() 函數,我們傳遞 !!state.dns 作為第一個參數,然後是一個 HTML 字符串作為第二個參數。

內容為:“當 state.dns 有一個值,渲染這個 HTML。”換句話說,一旦我們檢索到我們的 dns 來自我們的 getter 的值並將其放在我們組件的 state 上 ,我們想要渲染我們在這里傳遞的 HTML。如果我們仔細觀察,該 HTML 包含一個 HTML <table></table> 標籤呈現一個表格,輸出我們為我們的域獲得的 DNS 值。請注意,我們使用的是 ${} 再次插值輸出state.dns的內容 .同樣,從該值中,請注意,我們預期從服務器上的 getter 中返回的對像上的值相同:ipAddresses , cname , 和 nameserver .

因為我們希望這些值中的每一個都包含一個數組,為了使它們適合顯示,我們使用 JavaScript .join() 方法說“將此數組中的所有值連接成一個逗號分隔的字符串。”

為了達到這一點,在我們對 when() 的調用之上 ,我們可以看到一個簡單的 HTML <form></form><input /> 定義 和一個 <button></button> 類型為 submit .

/ui/pages/dns/index.js

events: {
  'submit form': (event, component) => {
    event.preventDefault();

    get('checkDNS', {
      input: {
        domain: event?.target?.domain?.value,
      },
    }).then((response) => {
      component.setState({ dns: response });
    }).catch((error) => {
      console.warn(error);
    });
  },
},

如果我們向上滾動我們的組件,我們可以看到這一切是如何在 events 中組合在一起的 財產。在這裡,我們為我們的組件定義 JavaScript 事件監聽器。為了處理我們輸入到 getter 中的域的傳遞,我們為 JavaScript submit 定義了一個事件監聽器 我們的 <form></form> 上的事件 標記。

在 Joystick 組件上定義事件偵聽器時,我們定義了我們想要偵聽的事件以及我們想要偵聽該事件的選擇器on 使用空格分隔的字符串 ('<event> <selector>' ) 作為我們的 events 的屬性 對象,然後為該屬性分配“處理程序”函數,以便在提供的選擇器(表單)上檢測到該事件(提交)時調用。

在此處定義的函數內部,我們接受兩個參數:event (觸發的 JavaScript DOM 事件)和 component (當前組件的實例)。

首先,因為我們正在處理 <form></form> 提交事件,我們要調用 event.preventDefault() 防止瀏覽器中的默認表單提交行為(這會觸發我們想要避免的頁面刷新)。

接下來是重要的部分,使用 get() 我們從 @joystick.js/ui 導入的函數 在頂部,我們調用我們的 checkDNS getter,傳遞 domain 的值 來自我們表單的輸入(JavaScript 自動通過它們的 name 分配輸入 event.target 的屬性 值,所以我們可以直接引用它們)。

最後,因為我們期望 get() 要返回一個 JavaScript Promise,我們添加一個 .then().catch() 回調我們的電話。對於 .then() 回調,我們期望取回我們從 getter 返回的值 作為 response .使用第二個參數傳遞給我們的事件處理程序 component ,我們稱之為.setState() 方法,設置 dns 我們之前給的默認值是 response .如果出現問題,在我們的 .catch() 回調,我們將錯誤註銷到控制台。

應該這樣做!現在,如果我們在瀏覽器中加載我們的頁面,我們應該能夠輸入一個域並查看它的 DNS 信息。

總結

在本教程中,我們學習瞭如何使用內置的 dns 連接一個簡單的 DNS 檢查器 打包在 Node.js 中。我們學習瞭如何獲取域名並將其傳遞給該包中定義的各種函數,檢索該域的 IP 地址、名稱服務器和 cname。我們還學習瞭如何連接 Joystick 組件,為我們提供一個用於檢索不同域的 DNS 信息的 GUI。


Tutorial JavaScript 教程
  1. 編寫自己的 npm 模塊

  2. 未捕獲的類型錯誤:無法讀取未定義的屬性(讀取“公司名稱”)JS 對象

  3. 為什麼 useReducer 是有線的?

  4. 如何使用 VS Code 調試 Netlify 無服務器 lambda 函數

  5. 使用 Vue3 組合 api 的可重用 Dialog 組件

  6. 使用 Codecept Gherkin 的多步驟定義文件

  7. 在 Angular 中退訂的更好方法

  1. 您今天將如何構建全棧 Node.js Web 應用程序?

  2. 不需要 Node 的 JavaScript 模板引擎

  3. JSON.stringify 將 toJSON 方法考慮在內

  4. 很棒的網頁效果

  5. 這個 Javascript 需要什麼?

  6. 使用 PM2 輕鬆部署節點應用程序

  7. PHP 7.3 中的新功能(現在在 Kinsta 上可用)

  1. 如何修復 nodemon 錯誤 - nodemon.ps1 無法加載,因為在此系統上禁用了運行腳本

  2. 前 6 個 Vue 管理模板

  3. 自定義 Amazon Cognito 用戶界面

  4. 使用 nice-modal-react 改進 React 中的模態管理