“艾在救援” 登录、查药功能实现(Ajax,Jason,javascript)

“艾在救援” 登录、查药功能实现(Ajax,JSON,JavaScript)

一、背景与问题

在开发“艾在救援”系统时,我们面临两个核心功能需求:用户登录认证和药品信息查询。这两个功能需要在保持页面交互性的同时,避免传统页面刷新带来的体验问题。

传统方案的痛点在于:

  1. 用户登录需要完整页面跳转,影响操作连贯性
  2. 药品查询需要重新加载整个页面,导致数据丢失
  3. 多次请求需要手动处理状态保持
  4. 无法实现实时数据更新

为了解决这些问题,我们选择使用Ajax技术实现异步通信,结合JSON数据格式进行数据交换。这种方案能够保持页面状态,实现局部更新,同时降低服务器负载。

二、基本原理

1. Ajax工作原理

Ajax(Asynchronous JavaScript and XML)通过XMLHttpRequest对象实现异步通信,其核心流程如下:

  1. 创建XMLHttpRequest对象
  2. 配置请求参数(URL、方法、头信息)
  3. 发起异步请求
  4. 监听状态变化事件
  5. 处理服务器响应数据

2. JSON数据格式

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,其特点包括:

  • 与JavaScript对象直接映射
  • 支持嵌套结构
  • 无类型声明
  • 易于阅读和编写

三、环境准备

1. 前端开发环境

  • 项目结构:

    /艾在救援
    │
    ├── index.html          # 主页面
    ├── login.html          # 登录页面
    ├── drug.html           # 药品查询页面
    ├── js/                 # JavaScript代码
    │   ├── login.js        # 登录功能
    │   ├── drug.js         # 药品查询功能
    │   └── utils.js        # 工具函数
    └── css/                # 样式文件

2. 后端模拟(Node.js示例)

// server.js
const express = require('express');
const app = express();
const port = 3000;

app.use(express.json());

// 模拟用户数据
const users = [
  { id: 1, name: '张三', password: '123456' },
  { id: 2, name: '李四', password: '654321' }
];

// 登录接口
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.name === username && u.password === password);
  
  if (user) {
    res.json({
      code: 200,
      message: '登录成功',
      data: user
    });
  } else {
    res.json({
      code: 401,
      message: '用户名或密码错误'
    });
  }
});

// 药品查询接口
app.get('/api/drugs', (req, res) => {
  const drugs = [
    { id: 1, name: '头孢丙烯', price: 25.5, stock: 100 },
    { id: 2, name: '阿奇霉素', price: 32.0, stock: 85 },
    { id: 3, name: '布洛芬', price: 15.0, stock: 200 }
  ];
  
  res.json({
    code: 200,
    message: '查询成功',
    data: drugs
  });
});

app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

四、核心实现

1. 登录功能实现(login.js)

// login.js
async function login(username, password) {
  const url = 'http://localhost:3000/api/login';
  
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ username, password })
    });
    
    const data = await response.json();
    
    if (data.code === 200) {
      // 保存登录状态
      localStorage.setItem('user', JSON.stringify(data.data));
      return true;
    } else {
      alert(data.message);
      return false;
    }
  } catch (error) {
    console.error('登录失败:', error);
    alert('网络错误,请重试');
    return false;
  }
}

关键代码解释:

  • 使用async/await处理异步请求
  • 设置正确的Content-Type头
  • 处理JSON响应数据
  • 使用localStorage保存用户状态
  • 错误处理机制

2. 药品查询功能(drug.js)

// drug.js
async function getDrugs() {
  const url = 'http://localhost:3000/api/drugs';
  
  try {
    const response = await fetch(url, {
      method: 'GET',
      headers: {
        'Accept': 'application/json'
      }
    });
    
    const data = await response.json();
    
    if (data.code === 200) {
      return data.data;
    } else {
      alert(data.message);
      return null;
    }
  } catch (error) {
    console.error('查询失败:', error);
    alert('网络错误,请重试');
    return null;
  }
}

关键代码解释:

  • GET请求的参数设置
  • 处理服务器响应
  • 错误处理机制
  • 返回结构化数据

3. 数据展示(index.html)

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>艾在救援</title>
    <script src="js/utils.js"></script>
</head>
<body>
    <div id="app">
        <h1>欢迎使用艾在救援</h1>
        <div id="user-info"></div>
        <button onclick="loadDrugs()">查看药品</button>
        <div id="drug-list"></div>
    </div>
    
    <script>
        // 检查登录状态
        window.onload = function() {
            const user = localStorage.getItem('user');
            if (user) {
                showUserInfo(JSON.parse(user));
            }
        };
        
        function showUserInfo(user) {
            const infoDiv = document.getElementById('user-info');
            infoDiv.innerHTML = `
                <p>欢迎,${user.name}</p>
                <p>登录时间:${new Date().toLocaleString()}</p>
            `;
        }
        
        async function loadDrugs() {
            const drugs = await getDrugs();
            if (drugs) {
                displayDrugs(drugs);
            }
        }
        
        function displayDrugs(drugs) {
            const listDiv = document.getElementById('drug-list');
            listDiv.innerHTML = `
                <h2>药品库存</h2>
                <ul>
                    ${drugs.map(drug => `
                        <li>${drug.name} - ¥${drug.price.toFixed(2)} (库存: ${drug.stock})</li>
                    `).join('')}
                </ul>
            `;
        }
    </script>
</body>
</html>

关键代码解释:

  • 页面加载时检查登录状态
  • 动态更新用户信息
  • 药品数据展示的动态生成
  • 调用getDrugs()函数获取数据

五、完整案例

1. 项目结构说明

/艾在救援
│
├── index.html          # 主页面
├── login.html          # 登录页面
├── drug.html           # 药品查询页面
├── js/                 # JavaScript代码
│   ├── login.js        # 登录功能
│   ├── drug.js         # 药品查询功能
│   └── utils.js        # 工具函数
└── css/                # 样式文件

2. 登录页面(login.html)

<!-- login.html -->
<!DOCTYPE html>
<html>
<head>
    <title>登录</title>
    <script src="js/login.js"></script>
</head>
<body>
    <div id="login-form">
        <h2>用户登录</h2>
        <input type="text" id="username" placeholder="用户名" required>
        <input type="password" id="password" placeholder="密码" required>
        <button onclick="handleLogin()">登录</button>
    </div>
    
    <script>
        async function handleLogin() {
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
            
            if (await login(username, password)) {
                window.location.href = 'index.html';
            }
        }
    </script>
</body>
</html>

3. 工具函数(utils.js)

// utils.js
function formatPrice(price) {
    return parseFloat(price).toFixed(2);
}

function formatDate(date) {
    return date.toLocaleString();
}

六、源码解析

1. 登录功能源码分析

async function login(username, password) {
    const url = 'http://localhost:3000/api/login';
    
    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ username, password })
        });
        
        const data = await response.json();
        
        if (data.code === 200) {
            // 保存登录状态
            localStorage.setItem('user', JSON.stringify(data.data));
            return true;
        } else {
            alert(data.message);
            return false;
        }
    } catch (error) {
        console.error('登录失败:', error);
        alert('网络错误,请重试');
        return false;
    }
}

关键点分析:

  • 使用fetch API发起HTTP请求
  • 设置Content-Type头确保数据格式正确
  • 处理服务器返回的JSON数据
  • 使用localStorage持久化保存用户信息
  • 异常处理机制确保程序健壮性

2. 药品查询源码分析

async function getDrugs() {
    const url = 'http://localhost:3000/api/drugs';
    
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Accept': 'application/json'
            }
        });
        
        const data = await response.json();
        
        if (data.code === 200) {
            return data.data;
        } else {
            alert(data.message);
            return null;
        }
    } catch (error) {
        console.error('查询失败:', error);
        alert('网络错误,请重试');
        return null;
    }
}

关键点分析:

  • GET请求的参数设置
  • 处理服务器响应数据
  • 错误处理机制
  • 返回结构化数据供前端展示

七、进阶使用

1. 增强登录功能

// login.js
async function login(username, password) {
    const url = 'http://localhost:3000/api/login';
    
    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ username, password })
        });
        
        const data = await response.json();
        
        if (data.code === 200) {
            // 保存登录状态
            localStorage.setItem('user', JSON.stringify(data.data));
            // 记录登录时间
            data.data.loginTime = new Date().toISOString();
            return true;
        } else {
            alert(data.message);
            return false;
        }
    } catch (error) {
        console.error('登录失败:', error);
        alert('网络错误,请重试');
        return false;
    }
}

2. 增强药品查询功能

async function getDrugs() {
    const url = 'http://localhost:3000/api/drugs';
    
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Accept': 'application/json'
            }
        });
        
        const data = await response.json();
        
        if (data.code === 200) {
            // 添加格式化后的价格
            data.data = data.data.map(drug => ({
                ...drug,
                formattedPrice: formatPrice(drug.price)
            }));
            return data.data;
        } else {
            alert(data.message);
            return null;
        }
    } catch (error) {
        console.error('查询失败:', error);
        alert('网络错误,请重试');
        return null;
    }
}

八、性能与工程实践

1. 性能优化策略

  1. 缓存机制:对频繁访问的数据进行缓存

    function getDrugs() {
     const cached = localStorage.getItem('drugCache');
     if (cached) {
         return JSON.parse(cached);
     }
     
     return fetchDrugs().then(data => {
         localStorage.setItem('drugCache', JSON.stringify(data));
         return data;
     });
    }
  2. 请求合并:合并多次请求减少网络开销

    let pendingRequest = null;
    function getDrugs() {
     if (pendingRequest) {
         return pendingRequest;
     }
     
     pendingRequest = new Promise((resolve, reject) => {
         fetchDrugs().then(resolve).catch(reject).finally(() => {
             pendingRequest = null;
         });
     });
     
     return pendingRequest;
    }
  3. 压缩数据:使用Gzip压缩响应数据

2. 安全风险分析

  1. CSRF攻击:需要添加CSRF token验证

    // 在登录请求中添加token
    const response = await fetch(url, {
     method: 'POST',
     headers: {
         'Content-Type': 'application/json',
         'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
     },
     body: JSON.stringify({ username, password, _token: csrfToken })
    });
  2. JSON注入:需要对返回数据进行验证

    function validateData(data) {
     if (typeof data !== 'object' || data === null) {
         throw new Error('Invalid data format');
     }
    }

九、常见问题与踩坑

1. 跨域问题(CORS)

错误示例:

fetch('http://localhost:3000/api/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
});

错误原因: 浏览器出于安全考虑会阻止跨域请求

解决办法:

  • 在服务器端添加CORS头

    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
  • 使用代理服务器

    // 本地开发服务器
    const proxy = require('http-proxy-middleware');
    
    module.exports = {
      devServer: {
          proxy: {
              '/api': {
                  target: 'http://localhost:3000',
                  changeOrigin: true,
                  pathRewrite: {
                      '^/api': ''
                  }
              }
          }
      }
    };

2. 数据格式错误

错误示例:

const data = await response.json();
console.log(data);

错误原因: 服务器返回的数据格式不正确

解决办法:

  • 添加类型检查

    function isJsonString(str) {
      try {
          JSON.parse(str);
          return true;
      } catch (e) {
          return false;
      }
    }
  • 添加错误处理

    try {
      const data = await response.json();
      if (!isJsonString(data)) {
          throw new Error('Invalid JSON response');
      }
    } catch (error) {
      console.error('数据解析失败:', error);
      alert('数据解析失败,请重试');
    }

十、最佳实践

1. 推荐的实现方案

  1. 使用fetch API:现代浏览器支持良好,易于使用
  2. 添加错误处理:确保程序健壮性
  3. 使用localStorage:持久化保存用户状态
  4. 格式化数据:提高可读性
  5. 添加防重放机制:防止重复请求

2. 推荐的代码规范

  • 使用ES6的async/await替代Promise链
  • 添加详细的错误日志
  • 使用TypeScript进行类型校验
  • 添加单元测试
  • 使用代码格式化工具(如Prettier)

十一、总结

本文深入探讨了"艾在救援"系统登录和药品查询功能的实现方案。通过使用Ajax技术和JSON数据格式,我们实现了异步通信,保持了页面状态,提升了用户体验。

在实现过程中,我们深入分析了Ajax的工作原理,探讨了JSON的数据结构,提供了完整的代码示例,并分析了性能优化和安全风险。同时,我们讨论了常见错误和解决办法,强调了代码规范的重要性。

在实际开发中,这种方案适用于需要保持页面状态、需要频繁更新数据、需要实时交互的场景。但需要注意避免过度使用,防止造成服务器负载过高。同时,需要特别注意安全风险,采取必要的防护措施。

最终,我们推荐使用这种方案来实现需要异步交互的功能,同时遵循最佳实践,确保代码的可维护性和可扩展性。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日