GET和POST方法

学习目标

  • 理解HTTP GET和POST方法的区别和应用场景
  • 掌握PHP中处理GET和POST请求的方法
  • 学会正确选择合适的HTTP方法
  • 了解安全性考虑和最佳实践

HTTP方法概述

HTTP协议定义了多种请求方法,其中最常用的是GET和POST。这两种方法在Web开发中用于向服务器发送数据。

GET方法

GET方法用于从服务器获取数据,其特点包括:

  • 数据通过URL参数传递
  • 有数据长度限制(通常2048字符)
  • 可以被浏览器缓存
  • 可以被收藏为书签
  • 不适合传输敏感信息

POST方法

POST方法用于向服务器提交数据,其特点包括:

  • 数据通过HTTP请求体传递
  • 没有数据长度限制
  • 不会被浏览器缓存
  • 不能被收藏为书签
  • 适合传输敏感信息

PHP中的超全局变量

PHP提供了两个重要的超全局变量来处理HTTP请求数据:

$_GET 超全局变量

包含通过GET方法传递的所有参数:

<?php
// 获取单个参数
$name = $_GET['name'];

// 检查参数是否存在
if (isset($_GET['name'])) {
    $name = $_GET['name'];
}

// 安全获取参数(带默认值)
$name = isset($_GET['name']) ? $_GET['name'] : 'Guest';

// 使用null合并运算符(PHP 7+)
$name = $_GET['name'] ?? 'Guest';
?>

$_POST 超全局变量

包含通过POST方法传递的所有参数:

<?php
// 获取POST数据
$username = $_POST['username'];
$password = $_POST['password'];

// 检查表单是否提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 处理表单数据
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
}
?>

GET方法详解

基本用法

<!-- HTML表单使用GET方法 -->
<form action="search.php" method="get">
    <label for="query">搜索:</label>
    <input type="text" id="query" name="query" placeholder="输入搜索关键词">
    <button type="submit">搜索</button>
</form>
<?php
// search.php - 处理GET请求
if (isset($_GET['query']) && !empty($_GET['query'])) {
    $searchQuery = trim($_GET['query']);

    // 安全处理:防止XSS攻击
    $searchQuery = htmlspecialchars($searchQuery, ENT_QUOTES, 'UTF-8');

    echo "您搜索的内容是:" . $searchQuery;

    // 这里可以执行数据库搜索操作
    // $results = searchDatabase($searchQuery);
} else {
    echo "请输入搜索关键词";
}
?>

URL参数构建

<?php
// 手动构建GET URL
$params = [
    'category' => 'books',
    'page' => 2,
    'sort' => 'price'
];

$queryString = http_build_query($params);
$url = 'products.php?' . $queryString;
// 结果:products.php?category=books&page=2&sort=price

// 重定向到带参数的URL
header('Location: ' . $url);
exit;
?>

分页示例

<?php
// 分页处理示例
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;

// 计算偏移量
$offset = ($page - 1) * $perPage;

// 构建分页链接
function buildPageLink($currentPage, $totalPages) {
    $links = '';

    // 上一页
    if ($currentPage > 1) {
        $links .= '<a href="?page=' . ($currentPage - 1) . '">上一页</a> ';
    }

    // 页码
    for ($i = 1; $i <= $totalPages; $i++) {
        if ($i == $currentPage) {
            $links .= '<strong>' . $i . '</strong> ';
        } else {
            $links .= '<a href="?page=' . $i . '">' . $i . '</a> ';
        }
    }

    // 下一页
    if ($currentPage < $totalPages) {
        $links .= '<a href="?page=' . ($currentPage + 1) . '">下一页</a>';
    }

    return $links;
}
?>

POST方法详解

基本表单处理

<!-- HTML表单使用POST方法 -->
<form action="register.php" method="post">
    <div>
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>
    </div>
    <div>
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required>
    </div>
    <div>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>
    </div>
    <button type="submit">注册</button>
</form>
<?php
// register.php - 处理POST请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 获取并清理POST数据
    $username = trim($_POST['username'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    // 基本验证
    $errors = [];

    if (empty($username)) {
        $errors[] = '用户名不能为空';
    } elseif (strlen($username) < 3) {
        $errors[] = '用户名至少3个字符';
    }

    if (empty($email)) {
        $errors[] = '邮箱不能为空';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = '邮箱格式不正确';
    }

    if (empty($password)) {
        $errors[] = '密码不能为空';
    } elseif (strlen($password) < 8) {
        $errors[] = '密码至少8个字符';
    }

    // 如果没有错误,处理注册逻辑
    if (empty($errors)) {
        // 密码加密
        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

        // 保存到数据库(示例)
        // saveUser($username, $email, $hashedPassword);

        echo "注册成功!欢迎," . htmlspecialchars($username);
    } else {
        // 显示错误信息
        foreach ($errors as $error) {
            echo '<p style="color: red;">' . htmlspecialchars($error) . '</p>';
        }
    }
}
?>

文件上传处理

<!-- 文件上传表单 -->
<form action="upload.php" method="post" enctype="multipart/form-data">
    <div>
        <label for="file">选择文件:</label>
        <input type="file" id="file" name="file" accept="image/*" required>
    </div>
    <div>
        <label for="description">文件描述:</label>
        <textarea id="description" name="description" rows="3"></textarea>
    </div>
    <button type="submit">上传文件</button>
</form>
<?php
// upload.php - 处理文件上传
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
        $file = $_FILES['file'];

        // 文件信息
        $fileName = $file['name'];
        $fileTmpName = $file['tmp_name'];
        $fileSize = $file['size'];
        $fileType = $file['type'];

        // 文件验证
        $allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
        $maxSize = 5 * 1024 * 1024; // 5MB

        if (!in_array($fileType, $allowedTypes)) {
            die('只允许上传 JPEG、PNG 和 GIF 图片');
        }

        if ($fileSize > $maxSize) {
            die('文件大小不能超过 5MB');
        }

        // 生成唯一文件名
        $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
        $newFileName = uniqid() . '.' . $fileExtension;
        $uploadDir = 'uploads/';

        // 确保上传目录存在
        if (!file_exists($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        // 移动文件到目标位置
        if (move_uploaded_file($fileTmpName, $uploadDir . $newFileName)) {
            $description = $_POST['description'] ?? '';

            // 保存文件信息到数据库
            // saveFileInfo($newFileName, $description, $fileType, $fileSize);

            echo "文件上传成功!文件名:" . htmlspecialchars($newFileName);
        } else {
            echo "文件上传失败";
        }
    } else {
        echo "请选择要上传的文件";
    }
}
?>

GET vs POST 对比

详细对比表

特性GETPOST
数据传递方式URL参数HTTP请求体
数据长度限制约2048字符无限制
安全性较低(数据可见)较高(数据隐藏)
缓存可缓存不可缓存
书签可收藏不可收藏
浏览器历史保存不保存
幂等性幂等非幂等
用途获取数据提交数据

选择指南

使用GET的场景:

  • 搜索功能
  • 分页
  • 过滤和排序
  • 获取资源
  • 书签友好的操作

使用POST的场景:

  • 用户注册/登录
  • 表单提交
  • 文件上传
  • 敏感数据传输
  • 状态改变操作

安全性考虑

1. 防止XSS攻击

<?php
// 始终对用户输入进行转义
$userInput = $_GET['input'] ?? '';
$safeOutput = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

echo $safeOutput;
?>

2. 防止CSRF攻击

<?php
session_start();

// 生成CSRF令牌
function generateCsrfToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

// 验证CSRF令牌
function validateCsrfToken($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

// 在表单中包含令牌
$csrfToken = generateCsrfToken();
?>

<!-- 在HTML表单中添加隐藏字段 -->
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">

<?php
// 处理表单时验证令牌
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $token = $_POST['csrf_token'] ?? '';
    if (!validateCsrfToken($token)) {
        die('CSRF验证失败');
    }

    // 处理表单数据...
}
?>

3. 输入验证和过滤

<?php
// 使用PHP内置过滤器
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 1, 'max_range' => 120]
]);

// 自定义过滤函数
function sanitizeInput($input, $type = 'string') {
    switch ($type) {
        case 'email':
            return filter_var($input, FILTER_SANITIZE_EMAIL);
        case 'int':
            return filter_var($input, FILTER_SANITIZE_NUMBER_INT);
        case 'string':
        default:
            return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
    }
}
?>

综合示例:搜索和过滤系统

HTML表单

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>产品搜索</title>
    <style>
        .search-form {
            margin: 20px 0;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input, select {
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
        button {
            background-color: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        .results {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <h1>产品搜索</h1>

    <!-- GET搜索表单 -->
    <form method="get" class="search-form">
        <div class="form-group">
            <label for="keyword">关键词:</label>
            <input type="text" id="keyword" name="keyword"
                   value="<?php echo htmlspecialchars($_GET['keyword'] ?? ''); ?>"
                   placeholder="输入产品关键词">
        </div>

        <div class="form-group">
            <label for="category">分类:</label>
            <select id="category" name="category">
                <option value="">全部分类</option>
                <option value="electronics" <?php echo (($_GET['category'] ?? '') === 'electronics') ? 'selected' : ''; ?>>
                    电子产品
                </option>
                <option value="books" <?php echo (($_GET['category'] ?? '') === 'books') ? 'selected' : ''; ?>>
                    图书
                </option>
                <option value="clothing" <?php echo (($_GET['category'] ?? '') === 'clothing') ? 'selected' : ''; ?>>
                    服装
                </option>
            </select>
        </div>

        <div class="form-group">
            <label for="minPrice">最低价格:</label>
            <input type="number" id="minPrice" name="min_price"
                   value="<?php echo htmlspecialchars($_GET['min_price'] ?? ''); ?>"
                   min="0" step="0.01">
        </div>

        <div class="form-group">
            <label for="maxPrice">最高价格:</label>
            <input type="number" id="maxPrice" name="max_price"
                   value="<?php echo htmlspecialchars($_GET['max_price'] ?? ''); ?>"
                   min="0" step="0.01">
        </div>

        <button type="submit">搜索</button>
        <a href="?">清除筛选</a>
    </form>

    <!-- 搜索结果将通过AJAX加载到这个区域 -->
    <div id="results" class="results">
        <!-- 结果内容 -->
    </div>
</body>
</html>

PHP处理逻辑

<?php
// search.php
function searchProducts($params) {
    // 模拟数据库查询
    $allProducts = [
        ['id' => 1, 'name' => '智能手机', 'category' => 'electronics', 'price' => 2999.99],
        ['id' => 2, 'name' => 'PHP编程指南', 'category' => 'books', 'price' => 89.90],
        ['id' => 3, 'name' => '运动T恤', 'category' => 'clothing', 'price' => 199.00],
        ['id' => 4, 'name' => '笔记本电脑', 'category' => 'electronics', 'price' => 5999.99],
        ['id' => 5, 'name' => 'JavaScript高级编程', 'category' => 'books', 'price' => 129.00],
    ];

    $filteredProducts = $allProducts;

    // 关键词过滤
    if (!empty($params['keyword'])) {
        $keyword = strtolower($params['keyword']);
        $filteredProducts = array_filter($filteredProducts, function($product) use ($keyword) {
            return strpos(strtolower($product['name']), $keyword) !== false;
        });
    }

    // 分类过滤
    if (!empty($params['category'])) {
        $filteredProducts = array_filter($filteredProducts, function($product) use ($params) {
            return $product['category'] === $params['category'];
        });
    }

    // 价格过滤
    if (!empty($params['min_price'])) {
        $minPrice = (float)$params['min_price'];
        $filteredProducts = array_filter($filteredProducts, function($product) use ($minPrice) {
            return $product['price'] >= $minPrice;
        });
    }

    if (!empty($params['max_price'])) {
        $maxPrice = (float)$params['max_price'];
        $filteredProducts = array_filter($filteredProducts, function($product) use ($maxPrice) {
            return $product['price'] <= $maxPrice;
        });
    }

    return array_values($filteredProducts);
}

// 处理搜索请求
$searchParams = [
    'keyword' => trim($_GET['keyword'] ?? ''),
    'category' => $_GET['category'] ?? '',
    'min_price' => $_GET['min_price'] ?? '',
    'max_price' => $_GET['max_price'] ?? ''
];

$results = searchProducts($searchParams);

// 显示搜索结果
if (!empty($results)) {
    echo '<h3>搜索结果(共 ' . count($results) . ' 个产品)</h3>';

    foreach ($results as $product) {
        echo '<div class="product" style="border: 1px solid #eee; padding: 15px; margin: 10px 0; border-radius: 5px;">';
        echo '<h4>' . htmlspecialchars($product['name']) . '</h4>';
        echo '<p>分类:' . htmlspecialchars($product['category']) . '</p>';
        echo '<p>价格:¥' . number_format($product['price'], 2) . '</p>';
        echo '<form method="post" action="add_to_cart.php" style="display: inline;">';
        echo '<input type="hidden" name="product_id" value="' . $product['id'] . '">';
        echo '<button type="submit" name="action" value="add_to_cart">加入购物车</button>';
        echo '</form>';
        echo '</div>';
    }
} else {
    echo '<p>没有找到符合条件的产品。</p>';
}
?>

最佳实践

  1. 正确选择HTTP方法:根据操作性质选择GET或POST
  2. 输入验证:始终验证和清理用户输入
  3. 安全性考虑:防范XSS、CSRF等攻击
  4. 错误处理:提供清晰的错误信息
  5. 用户体验:使用适当的反馈机制

总结

GET和POST方法是PHP Web开发中最基础也是最重要的概念。正确理解和使用这两种方法,掌握它们的特点和适用场景,是开发安全、高效的Web应用程序的基础。通过本章的学习,你应该能够根据具体需求选择合适的HTTP方法,并实现安全可靠的数据处理逻辑。