You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SolarWinds API数据拉取与双列表框展示问题求助

解决方案

一、先解决空白页问题(跨域+代码错误)

页面空白核心是两个问题:

  1. 请求头配置错误:Access-Control-Allow-Origin是服务端返回的响应头,不是前端请求头;rejectUnauthorized是Node.js环境参数,浏览器fetch不支持,这两个配置无效。
  2. 跨域限制:浏览器同源策略阻止IIS页面请求SolarWinds API,Postman不受同源策略限制所以能成功。

解决办法

方法1:SolarWinds配置CORS(推荐)

在SolarWinds服务器开启CORS支持,允许你的IIS域名(如http://your-iis-server)跨域请求,需添加响应头:

  • Access-Control-Allow-Origin: http://your-iis-server
  • 允许Authorization等自定义请求头

方法2:IIS反向代理(绕过跨域)

在IIS上配置反向代理,把SolarWinds API请求转发到本地路径(如/api/SolarWinds/...转发到https://serverHostName.net:17778/SolarWinds/...),前端请求同源地址避开跨域限制。

修改后的基础请求代码

// SolarwindsApiInteraction.js
const myHeaders = new Headers();
myHeaders.append("User-Agent", "curl/7.20.0 (i386-pc-win32) libcurl/7.20.0 OpenSSL/0.9.8l zlib/1.2.3");
myHeaders.append("Authorization", "Basic blahblahblhablhablahblah");

const requestOptions = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

// 用反向代理则替换为本地路径,否则用原API地址
fetch("/api/SolarWinds/InformationService/v3/Json/Query?query=SELECT+Caption,+IPAddress,+NodeID,+Uri+AS+[EntityUri]+FROM+Orion.Nodes+WHERE+Vendor+=+'Windows'", requestOptions)
  .then(response => {
    if (!response.ok) throw new Error(`HTTP错误!状态码: ${response.status}`);
    return response.json(); // 直接解析JSON,无需转文本
  })
  .then(result => {
    console.log(result);
    renderNodes(result.results); // 获取数据后渲染到页面
  })
  .catch(error => {
    console.error('请求失败:', error);
    document.body.innerHTML = `<p>数据加载失败: ${error.message}</p>`;
  });

二、构建完整功能页面

1. HTML结构(含双列表、搜索、时间选择、提交按钮)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>服务器维护设置</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>服务器维护窗口配置</h1>

    <!-- 搜索框 -->
    <div class="search-box">
        <input type="text" id="leftSearch" placeholder="搜索左侧服务器..." onkeyup="filterList('leftList', this.value)">
        <input type="text" id="rightSearch" placeholder="搜索已选服务器..." onkeyup="filterList('rightList', this.value)">
    </div>

    <!-- 双列表区域 -->
    <div class="list-container">
        <div class="list-wrapper">
            <h3>可用服务器</h3>
            <select id="leftList" multiple size="10"></select>
        </div>
        <div class="button-group">
            <button onclick="moveSelected('leftList', 'rightList')">→</button>
            <button onclick="moveSelected('rightList', 'leftList')">←</button>
        </div>
        <div class="list-wrapper">
            <h3>待维护服务器</h3>
            <select id="rightList" multiple size="10"></select>
        </div>
    </div>

    <!-- 时间选择器 -->
    <div class="time-container">
        <div>
            <label>维护开始时间:</label>
            <input type="datetime-local" id="startTime" required>
        </div>
        <div>
            <label>维护结束时间:</label>
            <input type="datetime-local" id="endTime" required>
        </div>
    </div>

    <!-- 提交按钮 -->
    <button id="submitBtn" onclick="submitMaintenance()" disabled>提交维护设置</button>

    <script src="SolarwindsApiInteraction.js"></script>
</body>
</html>

2. CSS样式(style.css)

body {
    font-family: Arial, sans-serif;
    max-width: 1000px;
    margin: 20px auto;
    padding: 0 20px;
}

.search-box {
    margin-bottom: 20px;
    display: flex;
    gap: 10px;
}

.search-box input {
    flex: 1;
    padding: 8px;
    font-size: 14px;
}

.list-container {
    display: flex;
    gap: 20px;
    align-items: center;
    margin-bottom: 30px;
}

.list-wrapper {
    flex: 1;
}

select {
    width: 100%;
    padding: 5px;
    font-size: 14px;
}

.button-group {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.time-container {
    display: flex;
    gap: 30px;
    margin-bottom: 20px;
}

.time-container div {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

#submitBtn {
    padding: 10px 20px;
    font-size: 16px;
    cursor: pointer;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
}

#submitBtn:disabled {
    background-color: #ccc;
    cursor: not-allowed;
}

3. 完整JS功能代码(SolarwindsApiInteraction.js)

// 全局存储服务器数据
let serverData = [];

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
    fetchServerData();
    // 监听右侧列表变化,控制提交按钮状态
    document.getElementById('rightList').addEventListener('change', toggleSubmitBtn);
});

// 请求SolarWinds服务器数据
function fetchServerData() {
    const myHeaders = new Headers();
    myHeaders.append("User-Agent", "curl/7.20.0 (i386-pc-win32) libcurl/7.20.0 OpenSSL/0.9.8l zlib/1.2.3");
    myHeaders.append("Authorization", "Basic blahblahblhablhablahblah");

    const requestOptions = {
        method: 'GET',
        headers: myHeaders,
        redirect: 'follow'
    };

    // 替换为你的API地址或代理地址
    const apiUrl = "https://serverHostName.net:17778/SolarWinds/InformationService/v3/Json/Query?query=SELECT+Caption,+IPAddress,+NodeID,+Uri+AS+[EntityUri]+FROM+Orion.Nodes+WHERE+Vendor+=+'Windows'";
    
    fetch(apiUrl, requestOptions)
        .then(response => {
            if (!response.ok) throw new Error(`请求失败: ${response.status} ${response.statusText}`);
            return response.json();
        })
        .then(result => {
            serverData = result.results;
            renderNodes(serverData);
        })
        .catch(error => {
            console.error('数据加载失败:', error);
            document.body.innerHTML = `<p>数据加载失败,请检查网络或API配置: ${error.message}</p>`;
        });
}

// 渲染服务器到左侧列表
function renderNodes(nodes) {
    const leftList = document.getElementById('leftList');
    leftList.innerHTML = '';
    nodes.forEach(node => {
        const option = document.createElement('option');
        option.value = node.NodeID;
        option.textContent = node.Caption;
        // 存储完整数据到option的dataset属性
        option.dataset.entityUri = node.EntityUri;
        option.dataset.ip = node.IPAddress;
        leftList.appendChild(option);
    });
}

// 搜索过滤列表项
function filterList(listId, keyword) {
    const list = document.getElementById(listId);
    const options = list.querySelectorAll('option');
    keyword = keyword.toLowerCase();
    options.forEach(option => {
        const text = option.textContent.toLowerCase();
        option.style.display = text.includes(keyword) ? 'block' : 'none';
    });
}

// 移动选中的服务器项
function moveSelected(fromListId, toListId) {
    const fromList = document.getElementById(fromListId);
    const toList = document.getElementById(toListId);
    // 复制选中选项(避免直接移动导致数据丢失)
    const selectedOptions = Array.from(fromList.selectedOptions);
    selectedOptions.forEach(option => {
        const newOption = document.createElement('option');
        newOption.value = option.value;
        newOption.textContent = option.textContent;
        newOption.dataset.entityUri = option.dataset.entityUri;
        newOption.dataset.ip = option.dataset.ip;
        toList.appendChild(newOption);
        option.remove();
    });
    toggleSubmitBtn();
}

// 切换提交按钮启用状态
function toggleSubmitBtn() {
    const rightList = document.getElementById('rightList');
    const submitBtn = document.getElementById('submitBtn');
    submitBtn.disabled = rightList.options.length === 0;
}

// 提交维护设置到SolarWinds API
function submitMaintenance() {
    const rightList = document.getElementById('rightList');
    const startTime = document.getElementById('startTime').value;
    const endTime = document.getElementById('endTime').value;

    // 收集待维护服务器的EntityUri
    const entityUris = Array.from(rightList.options).map(option => option.dataset.entityUri);

    if (!startTime || !endTime || entityUris.length === 0) {
        alert('请填写完整信息并选择服务器');
        return;
    }

    // 构造提交数据(格式需符合SolarWinds API要求)
    const payload = {
        entities: entityUris,
        startTime: new Date(startTime).toISOString(),
        endTime: new Date(endTime).toISOString(),
        maintenanceType: "Planned"
    };

    const submitHeaders = new Headers();
    submitHeaders.append("Content-Type", "application/json");
    submitHeaders.append("Authorization", "Basic blahblahblhablhablahblah");

    // 替换为SolarWinds创建维护窗口的API地址
    const submitUrl = "https://serverHostName.net:17778/SolarWinds/InformationService/v3/Json/Invoke/Orion.MaintenanceWindow/CreateMaintenanceWindow";

    fetch(submitUrl, {
        method: 'POST',
        headers: submitHeaders,
        body: JSON.stringify(payload)
    })
    .then(response => {
        if (!response.ok) throw new Error(`提交失败: ${response.status}`);
        return response.json();
    })
    .then(result => {
        alert('维护设置提交成功!');
        // 提交后清空右侧列表
        rightList.innerHTML = '';
        toggleSubmitBtn();
    })
    .catch(error => {
        alert(`提交失败: ${error.message}`);
        console.error('提交错误:', error);
    });
}

三、调试注意事项

  1. 打开浏览器开发者工具(F12),查看「控制台」和「网络」标签,确认请求是否成功、是否存在跨域或证书报错。
  2. 若遇SSL证书错误,需在SolarWinds服务器配置可信SSL证书,测试环境下可临时忽略(不建议生产环境)。
  3. 确保Authorization的Basic令牌有效,Postman能正常请求则令牌无问题。

内容的提问来源于stack exchange,提问作者Anirban Banerjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:12:19