PHP-Web服务端-05-HTTP-JSON

浏览器的请求流程

PHP-Web服务端-05-HTTP-JSON

HTTP

HTTP(HyperText Transfer Protocol,超文本传输协议)最早就是计算机与计算机之间沟通的一种标准协议,这种协议限制了通讯内容的格式以及各项内容的含义

随着时代的发展,技术的变迁,这种协议现在广泛的应用在各种领域,也不仅仅局限于计算机与计算机之间,手机、电视等各种智能设备很多时候都在使用这种协议通讯,所以一般现在称 HTTP 为端与端之间的通讯协议。

应用软件架构一般分为两类:
	B/S 架构:Browser(浏览器) ←→ Server(服务器),这种软件都是通过浏览器访问一个网站使用,服务器提供数据存储等服务。
	C/S 架构:Client(客户端) ←→ Server(服务器),这种软件通过安装一个软件到电脑,然后使用,服务器提供数据存储等服务。

概要

约定内容
  • 请求 / 响应报文格式
  • 请求方法 —— GET / POST
  • 响应状态 —— 200 / 404 / 302 / 304
  • 预设的请求 / 响应头
约定形式
  1. 客户端通过随机端口与服务端某个固定端口(一般为80–http;443–https)建立连接 三次握手
  2. 客户端通过这个连接发送请求(包含请求的数据包)到服务端(这里的请求是名词)
  3. 服务端监听端口得到的客户端发送过来的请求
  4. 服务端通过连接响应给客户端状态和内容(响应报文)

核心概念

报文
  • 请求报文
    PHP-Web服务端-05-HTTP-JSON
    请求行

      GET /demo.php HTTP/1.1
      请求方式 + 空格 + 请求路径 + 空格 + HTTP 协议版本
    

    请求头

      客户端想要告诉服务端的一些额外信息,以下为常见的请求头:
    

    PHP-Web服务端-05-HTTP-JSON请求体

      这次请求客户端想要发送给服务端的数据正文,一般在 GET 请求时很少用到,因为 GET 请求主观上都是去“拿东西”。
    
  • 响应报文
    PHP-Web服务端-05-HTTP-JSON
    状态行
    HTTP/1.1 200 OK
    HTTP 协议版本 + 空格 + 状态码 + 空格 + 状态描述

    响应头
    服务端想要告诉客户端的一些额外信息,常见的有以下:
    PHP-Web服务端-05-HTTP-JSON
    响应体

    这次请求服务端想要返回给客户端的数据正文,一般返回的都是 HTML,也可以返回 JavaScript 或者 CSS(需要修改响应头中的响应类型)。

  • 应用场景—header()方法

    1.设置响应文件类型

      header('Content-Type: text/css');	----- 页面默认为text/html
      HTTP MIME type 指的就是 像 text/css text/html text/plain applcation/javascript
    

    2.重定向(跳转到其他网页)

      这里是在 响应头中添加一个 location 的头信息
      // 客户端浏览器在接收到这个头信息过后会自动跳转到 指定的地址
      // 切记不能循环重定向
      header('Location: https://www.baidu.com');
    

    3.下载文件

      // 让文件下载
      header('Content‐Type: application/octet‐stream');
      // 设置默认下载文件名
      header('Content‐Disposition: attachment; filename=demo.txt');
    

    4.图片防盗链

      通过判断请求来源 Referer 是否为本网站从而区分是否是合法请求
    
请求方式
  • GET
    字面意思:拿,获取
  • POST
    字面意思:发,给
  • 对比 GET 与 POST
    PHP-Web服务端-05-HTTP-JSON
状态码

了解即可,不用刻意去记忆,用多了自然就忘不了。
http://www.w3school.com.cn/tags/html_ref_httpmessages.asp

状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。

1xx:指示信息 —— 表示请求已接收,继续处理。
2xx:成功 —— 表示请求已被成功接收、理解、接受。
3xx:重定向 —— 要完成请求必须进行更进一步的操作。
4xx:客户端错误 —— 请求有语法错误或请求无法实现。
5xx:服务器端错误 —— 服务器未能实现合法的请求。
-------------------------------------------------------------------------------
常见状态代码、状态描述的说明如下。
200 OK:客户端请求成功。
400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized:请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用。
403 Forbidden:服务器收到请求,但是拒绝提供服务。
404 Not Found:请求资源不存在,举个例子:输入了错误的URL。
500 Internal Server Error:服务器发生不可预期的错误。
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。

JSON

JSON(JavaScript Object Notation) 是一种通过普通字符串描述数据的手段,用于表示有结构的数据。类似于编程语言中字面量的概念,语法上跟 JavaScript 的字面量非常类似

数据类型

null、string、number、boolean、object、array

注意
	1. JSON 中属性名称必须用双引号包裹
	2. JSON 中表述字符串必须使用双引号
	3. JSON 中不能有单行或多行注释 
	4. JSON 没有 undefined 这个值
	

[
	{
	"id": "59d632855434e",
	"title": "错过",
	"artist": "梁咏琪",
	"images": ["/uploads/img/1.jpg"],
	"source": "/uploads/mp3/1.mp3"
	},
	{
	"id": "59d632855434f",
	"title": "开始懂了",
	"artist": "孙燕姿",
	"images": ["/uploads/img/2.jpg"],
	"source": "/uploads/mp3/2.mp3"
	}
]
php解析JSON数据
  • JSON===》对象
    $contents = file_get_contents(‘storage.json’); //获取JSON数据
    // $contents => JSON 格式的字符串
    // 把 JSON 格式的字符串转换为对象的过程叫做反序列化
    // json_decode 默认反序列化时 将 JSON 中的对象转换为 PHP 中 stdClass 类型的对象
    $data = json_decode($contents, true); //加true–一关联数组方式返回对象

  • 对象===》JSON
    json_encode()

参考链接

HTML 中的 form 标签:http://www.w3school.com.cn/html/html_forms.asp
PHP 中处理表单:http://www.w3school.com.cn/php/php_forms.asp
Emmet 手册:https://docs.emmet.io/cheat-sheet/

拓展

uniqid()	//获取一个唯一的ID

<form enctype="multipart/form-data"(文件上传) autocomplete="off"(关闭cookie提示) multiple(多选)>
	<!-- accept 可以限制文件域能够选择的文件种类,值是 MIME Type -->
	<input type="file" class="form-control" id="source" name="source" accept="audio/*"(限制上传文件的类型)>
</form>

案例音乐列表源码

设计好想要完成项目的架构–方法功能流程—再着手实现具体功能
服务端保存数据时的路径一定要设为绝对路径

需要引用bootstrip4
列表页面
<?php
// PHP 的价值:
// 通过执行某些PHP代码获取到指定的数据,填充到HTML的指定位置
//获取json文件内内容
$json = file_get_contents('data.json');
//将内容反序列转化为对象
$data = json_decode($json, true);
if (!$data) {
  // JSON 格式不正确
  exit('数据文件异常');
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>音乐列表</title>
  <link rel="stylesheet" href="bootstrap.css">
</head>
<body>
  <div class="container py-5">
    <h1 class="display-4">音乐列表</h1>
    <hr>
    <div class="mb-3">
      <a href="add.php" class="btn btn-secondary btn-sm">添加</a>
    </div>
    <table class="table table-bordered table-striped table-hover">
      <thead class="thead-dark">
        <tr>
          <th class="text-center">标题</th>
          <th class="text-center">歌手</th>
          <th class="text-center">海报</th>
          <th class="text-center">音乐</th>
          <th class="text-center">操作</th>
        </tr>
      </thead>
      <tbody class="text-center">
        <?php foreach ($data as $item): ?>
        <tr>
          <td class="align-middle"><?php echo $item['title']; ?></td>
          <td class="align-middle"><?php echo $item['artist']; ?></td>
          <td class="align-middle">
            <?php foreach ($item['images'] as $src): ?>
            	<img src="<?php echo $src; ?>" alt="">
            <?php endforeach ?>
          </td>
          <td class="align-middle"><audio src="<?php echo $item['source']; ?>" controls></audio></td>
          <td class="align-middle"><a class="btn btn-danger btn-sm" href="delete.php?id=<?php echo $item['id']; ?>">删除</a></td>
        </tr>
        <?php endforeach ?>
      </tbody>
    </table>
  </div>
</body>
</html>
添加音乐信息页面
<?php
function add () {
  // 目标:接收客户端提交的数据和文件,最终保存到数据文件中
  $data = array(); // 准备一个空的容器,用来装最终要保存的 数据
  $data['id'] = uniqid();
  
  // 1. 接收提交的文本内容
  // ===================================================
  if (empty($_POST['title'])) {
    $GLOBALS['error_message'] = '请输入音乐标题';
    return;
  }
  if (empty($_POST['artist'])) {
    $GLOBALS['error_message'] = '请输入歌手名称';
    return;
  }

  // 记下 title 和 artist
  $data['title'] = $_POST['title'];
  $data['artist'] = $_POST['artist'];

  // 2. 接收图片文件
  // =======================================================
  // 如何接收单个文件域的多文件上传???
  if (empty($_FILES['images'])) {
    $GLOBALS['error_message'] = '请正常使用表单';
    return;
  }

  $images = $_FILES['images'];
  // 准备一个容器装所有的海报路径
  $data['images'] = array();

  // 遍历这个文件域中的每一个文件(判断是否成功、判断类型、判断大小、移动到网站目录中)
  for ($i = 0; $i < count($images['name']); $i++) {
    // $images['error'] => [0, 0, 0]
    if ($images['error'][$i] !== UPLOAD_ERR_OK) {
      $GLOBALS['error_message'] = '上传海报文件失败';
      return;
    }

// 类型的校验
// $images['type'] => ['image/png', 'image/jpg', 'image/gif']
if (strpos($images['type'][$i], 'image/') !== 0) {
  $GLOBALS['error_message'] = '上传海报文件格式错误';
  return;
}

// TODO: 文件大小的判断
if ($images['size'][$i] > 1 * 1024 * 1024) {
  $GLOBALS['error_message'] = '上传海报文件过大';
  return;
}

// 移动文件到网站范围之内
$dest = '../uploads/' . uniqid() . $images['name'][$i];
if (!move_uploaded_file($images['tmp_name'][$i], $dest)) {
  $GLOBALS['error_message'] = '上传海报文件失败2';
  return;
}

$data['images'][] = substr($dest, 2);
}

  // 3. 接收音乐文件
  // =======================================================
  if (empty($_FILES['source'])) {
    $GLOBALS['error_message'] = '请正常使用表单';
    return;
  }

  $source = $_FILES['source'];
  // => { name: , tmp_name .... }

  // 判断是否上传成功
  if ($source['error'] !== UPLOAD_ERR_OK) {
    $GLOBALS['error_message'] = '上传音乐文件失败1';
    return;
  }
  // 判断类型是否允许
  $source_allowed_types = array('audio/mp3', 'audio/wma');
  if (!in_array($source['type'], $source_allowed_types)) {
    $GLOBALS['error_message'] = '上传音乐文件类型错误';
    return;
  }
  // 判断大小
  if ($source['size'] < 1 * 1024 * 1024) {
    $GLOBALS['error_message'] = '上传音乐文件过小';
    return;
  }
  if ($source['size'] > 10 * 1024 * 1024) {
    $GLOBALS['error_message'] = '上传音乐文件过大';
    return;
  }
  // 移动
  $target = '../uploads/' . uniqid() . '-' . $source['name'];
  if (!move_uploaded_file($source['tmp_name'], $target)) {
    $GLOBALS['error_message'] = '上传音乐文件失败2';
    return;
  }
  // 将数据装起来
  // 保存数据的路径一定使用绝对路径存
  $data['source'] = substr($target, 2);

  // 4. 将数据加入到原有数据中
  $json = file_get_contents('data.json');
  $old = json_decode($json, true);
  array_push($old, $data);
  $new_json = json_encode($old);
  file_put_contents('data.json', $new_json);

  // 5. 跳转
  header('Location: list.php');
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  add();
}

?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>添加新音乐</title>
  <link rel="stylesheet" href="bootstrap.css">
</head>
<body>
  <div class="container py-5">
    <h1 class="display-4">添加新音乐</h1>
    <hr>

<?php if (isset($error_message)): ?>
<div class="alert alert-danger">
  <?php echo $error_message; ?>
</div>
<?php endif ?>

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
  <div class="form-group">
    <label for="title">标题</label>
    <input type="text" class="form-control" id="title" name="title">
  </div>
  <div class="form-group">
    <label for="artist">歌手</label>
    <input type="text" class="form-control" id="artist" name="artist">
  </div>
  <div class="form-group">
    <label for="images">海报</label>
    <!-- multiple 可以让一个文件域多选 -->
    <input type="file" class="form-control" id="images" name="images[]" accept="image/*" multiple>
  </div>
  <div class="form-group">
    <label for="source">音乐</label>
    <!-- accept 可以设置两种值分别为  MIME Type / 文件扩展名 -->
    <input type="file" class="form-control" id="source" name="source" accept="audio/*">
  </div>
  <button class="btn btn-primary btn-block">保存</button>
</form>
</div>
</body>
</html>
删除音乐信息
<?php

// 如何知道客户端想要删除哪一个???
// 通过客户端在URL地址中的问号参数的不同来辨别要删除的数据

// 接收 URL 中的不同的 ID
if (empty($_GET['id'])) {
  // 没有传递必要的参数
  exit('<h1>必须指定参数</h1>');
}

$id = $_GET['id'];

// 找到要删除的数据
$data = json_decode(file_get_contents('data.json'), true);
foreach ($data as $item) {
  // 不是我们要的之间找下一条
  if ($item['id'] !== $id) continue;
  // $item => 我们要删除的那一条数据

  // 从原有数据中移除
  $index = array_search($item, $data);
  array_splice($data, $index, 1);

  // 保存删除指定数据过后的内容
  // echo '<pre>';
  // var_dump($data);
  // echo '</pre>';
  $json = json_encode($data);
  file_put_contents('data.json', $json);

  // 跳转回列表页
  header('Location: list.php');
}