HTTP-запросы, которые я знаю

HTTP Ajax

Все знакомы с HTTP, но многие тонкости HTTP не известны многим, в этой статье будут рассмотрены некоторые важные моменты, которые легко упустить из виду.

Во-первых, как использоватьНативный JS пишет GET-запросШерстяная ткань? Следующий код, всего 3 строки:

let xhr = new XMLHttpRequest();
xhr.open("GET", "/list");
xhr.send();

Первый параметр xhr.open — это метод запроса, второй параметр — это URL-адрес запроса, а затем отправить его.

Если вам нужно добавить параметры запроса, если вы используете ajax jQuery, это пишется так:

$.ajax({
    url: "/list",
    data: {
        page: 5
    }
});

Если вы используете натив, вы должны написать его в URL-адресе запроса, то есть во втором параметре open:

И параметры должны быть экранированы, как показано в следующем коде:

function ajax (url, data) {
    let args = [];
    for (let key in data) {
        // 参数需要转义
      args.push(`${encodeURIComponent(key)} = 
                                     ${encodeURIComponent(data[key])}`);
    }
    let search = args.join("&");
    // 判断当前url是否已有参数
    url += ~url.indexOf("?") ? `&${search}` : `?${search}`;

    let xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.send();
}

Тогда почему бы не использовать jq? Поскольку jq сделал это за нас, ajax jq поддерживает параметр processData, который по умолчанию имеет значение true:

$.ajax({
    url: "/list",
    data: {
        page: 5
    },
    processData: true
});

Функция этого параметра заключается в обработке входящих данных, как показано в исходном коде jq:

Если данные переданы, а processData имеет значение true, а данные не являются строкой, параметр param будет скорректирован для обработки данных. Затем давайте посмотрим, как реализована эта функция param:

Видно, что он тоже похож на ajax, который я реализовал сам: escape-последовательности ключа и значения соединяются с помощью "=", затем помещаются в массив и, наконец, снова соединяются. Разница в том, что его логика суждения сложнее, чем у меня, он вызовет функцию buildParams для обработки ключ/значение, потому что значение может быть массивом или объектом. Если значение является объектом, то непосредственное кодирование станет экранированием «[object Object]»:

Поэтому, когда buildParams обрабатывает каждый ключ/значение, он сначала определяет, является ли текущее значение массивом или объектом, как показано на следующем рисунке:

Если это массив, то каждый элемент массива станет отдельным полем запроса, а его ключом будет ключ родителя, а индекс массива, например {ids: [1, 2, 3]}, будет написан по буквам как: ids[0]=1, ids[1] = 2, ids[2] = 3, если это объект, суффикс ключа является ключом дочернего объекта, например {user: {id: 1333, имя: "инь"}} будет записано как: user[id]=1333, user[name]=yin, в противном случае он считается простым типом, напрямую настройте добавление, определенное функцией param, и нажмите его на этот массив. Здесь используется рекурсивный вызов, и значение ключа будет непрерывно указываться до тех пор, пока значение не станет обычной переменной, а затем будет достигнута последняя логика else.

То есть следующий код:

$.ajax({
    url: "/list",
    data: {
        user: {
            name: "yin",
            age: 18
        }
    },
});

URL-адрес, который будет прописан:

/list?user[name]=yin&user[age]=18

Обратите внимание, что квадратные скобки выше не экранированы. И если это массив:

$.ajax({
    url: "/list",
    data: {
        ids: [1, 2, 3]
    },
});

Собранный URL-адрес запроса:

/list?ids[0]=1&ids[1]=2&ids[2]=3

Если серверная часть использует инфраструктуру Java Spring MVC, она понимает этот формат. После того, как инфраструктура получит такие параметры, она сгенерирует массив и передаст его бизнес-коду. Бизнес-коду не нужно заботиться о том, как обращаться с такими параметрами. Другие фреймворки должны быть похожими.


Как написать POST-запрос с помощью собственного JS? Как показано ниже:

Параметры POST-запроса помещаются не в url, а в send, то есть в тело запроса. Вы можете спросить: нельзя ли указать адрес? Я собираюсь поставить URL. Если вы своевольны, то да, предпосылка заключается в том, что структура http, используемая бэкэндом, может извлекать данные в URL-адресе, потому что она обязательно получит URL-адрес и тело запроса, поэтому это зависит от того, как он обрабатывает, согласно http Standard, если метод запроса POST, то он должен браться из тела запроса, а не браться из поиска url.Конечно, его можно изменить, а можно брать и то, и другое.

Затем мы обнаружим, что запрошенный тип mime — text/plain:

И при просмотре параметров запроса построчно может отображаться не то, что вы обычно видите:

Почему это? Это потому, что мы не установили его Content-Type, как в следующем коде:

let xhr = new XMLHttpRequest();
xhr.open("POST", "/add");
xhr.setRequestHeader("Content-type", 
                           "application/x-www-form-urlencoded");
xhr.send("id=5&name=yin");

Если задать для Content-Type значение x-www-form-urlencoded, то при проверке Chrome также будет отображать его по полям:

Это также тип контента jq по умолчанию:

Это один из наиболее часто используемых методов кодирования запросов, он поддерживает методы GET/POST и др. Он характеризуется тем, что: все данные представлены в виде пар ключ-значение ключ1=значение1&ключ2=значение2, а специальные символы необходимо экранировать в числа utf-8, такие как пробелы, станут %20:

Так как в utf-8 китайскому нужно занимать 3 байта, то у него 3% знаков.


Мы просто xhr.отправляем строку, что, если мы отправим объект? Как показано в следующем коде:

let xhr = new XMLHttpRequest();
xhr.open("POST", "/add");
xhr.send({id:5, name: "yin"});

При проверке консоли это выглядит так:

То есть фактически вызывается метод toString объекта Object. Таким образом, вы можете видеть, что вОтправляемые данные необходимо преобразовать в строку.

В дополнение к строкам send также поддерживает такие форматы, как FormData/Blob, например:

let form = $("form")[0];
xhr.send(new FormData(form));

Но в итоге он преобразуется в строку и отправляется.


Давайте рассмотрим другие форматы запросов, такие какGithub REST APIЯвляется ли использование формата json для отправки запроса:

На данный момент, если требуется изменить формат на json, необходимо указатьContent-Type — это приложение/json., а затем отправьте данные для строковой обработки:

let xhr = new XMLHttpRequest();
xhr.open("POST", "/add");
xhr.setRequestHeader("Content-type", "application/json");
let data = {id:5, name: "yin"};
xhr.send(JSON.stringify(data));

Если вы используете jq, вы можете сделать это:

$.ajax({
    processData: false,
    data: JSON.stringify(data),
    contentType: "application/json"
});

В это время processData имеет значение false, указывая jq не обрабатывать данные, то есть в форме ключ1=значение1&ключ2=значение2, просто отправить данные, переданные ему напрямую.

Мы можем сравнить преимущества и недостатки json и urlencoded.Недостаток json в том, что нагрузка на синтаксический анализ значительно выше, чем у split("&"), но преимущество json в том, что он более лаконичен при выражении сложных структур, например, двумерный массив (m*n) нужно разбить на m*n полей в urlencoded, а json — нет. Таким образом, условно говоря, если структура данных запроса относительно проста, должно быть более выгодно использовать обычно используемый urlencoded, и более выгодно использовать json, когда он более сложный. Вообще говоря, urlencoded используется чаще.


И третья распространенная кодировкаmultipart/form-data, который также можно использовать для отправки запросов, как показано в следующем коде:

let formData = new FormData();
formData.append("id", 5); // 数字5会被立即转换成字符串 "5"
formData.append("name", "#yin");
// formData.append("file", input.files[0]);
let xhr = new XMLHttpRequest();
xhr.open("POST", "/add");
xhr.send(formData);

Обычно он используется для загрузки файлов, и загрузка файлов должна быть именно в этом формате. Содержимое, отправленное приведенным выше кодом, показано на следующем рисунке:

Каждое поле отделяется случайной строкой, чтобы гарантировать, что эта строка не появится в отправленном содержимом, так что отправленное содержимое не нужно экранировать, потому что, если файл большой, экранирование занимает значительное время, объем также может расти в геометрической прогрессии.


Тогда мы обсудим другой вопрос, мы знаем, что ввод данных URL-запроса в адресную строку браузера — это GET-запрос, и GET-запрос, который мы используем в коде ajax, — это тоже GET.В чем разница между GET и ajax GET для URL-адреса доступа к браузеру??

Чтобы иметь возможность наблюдать GET, отправленный самим браузером, вам нужно использовать инструмент захвата пакетов, чтобы увидеть, как выглядит GET, как показано на следующем рисунке:

GET, отправляемый самим браузером, имеет очевидную особенность — он установит поле Accept заголовка http-запроса и поставит text/html на первое место, то есть больше всего ожидает получить формат html. Отображение динамического захвата пакетов ajax выглядит следующим образом:

Можно видеть, что запрос на получение, доступ к которому осуществляется с помощью адресной строки, и запрос на получение с использованием ajax, по сути, одинаковы, но с помощью ajax мы можем установить заголовок HTTP-запроса, а используя адресную строку для доступа к заголовку запроса по умолчанию, добавленному браузером. .


Выше показан пакет, захваченный с помощью http. Мы можем видеть полный URL-адрес запроса, включая параметры запроса. Если это пакет, захваченный с помощью https, параметры, размещенные в URL-адресе с помощью GET, не видны:

То есть сообщение запроса https зашифровано, включая запрошенный uri и т. д., которые необходимо расшифровать, прежде чем их можно будет увидеть. Означает ли это, что GET с использованием https также безопасен, а не httpsРазве POST не будет безопаснее, чем GET?

Давайте сначала посмотрим на сообщение HTTP-запроса, как показано на следующем рисунке:

Если вы используете инструмент захвата пакетов, вы можете увидеть, что пакеты запросов действительно упорядочены в соответствии с приведенным выше рисунком, как показано в GET, показанном на рисунке:

И POST выглядит так:

По сути, GET/POST — это то же самое, за исключением того, что GET помещает данные в URL-адрес, а POST — в тело запроса. Другой момент заключается в том, что URL-адрес ограничен по длине, включая браузеры и получаемые службы, такие как nginx, в то время как тело запроса не ограничено (для браузеров нет ограничений, но обычно nginx получает ограничения), а данные POST поддерживают различные форматы кодирования. .

Тем не менее, POST безопаснее GET по следующим причинам:

  1. Параметр GET помещается в url, и пользователь может сохранить его как закладку и распространить ссылку.Если параметр содержит конфиденциальные данные, такие как логин-пароль, они могут быть утеряны.
  2. Когда поисковая система сканирует веб-сайт, если запрос на изменение базы данных поддерживает GET, очень вероятно, что база данных будет изменена поисковой системой непреднамеренно.
  3. Теги типа script/img это GET запросы, что будет удобнее для подделки межсайтовых запросов.Ввод в адресную строку браузера тоже GET, что тоже удобно для модификации запросов


Затем обсудитекод состояния ответа на запрос. Многие люди знают о 200, 404 и 500, но они могут мало знать о других. Здесь я перечисляю некоторые часто используемые коды состояния.

1. 301 Постоянный перевод

Если вы хотите изменить доменное имя, вы можете использовать 301. Например, предыдущее доменное имя было www.renfed.com, а затем новое доменное имя было изменено на www.rrfed.com.Я надеюсь, что пользователи смогут автоматически переходить на новое доменное имя, когда они посещают старое доменное имя, то вы можете использовать nginx для возврата 301:

server {
    listen       80;
    server_name  www.rrfed.com;
    root         /home/fed/wordpress;
    return       301 https://www.rrfed.com$request_uri;
}

После того, как браузер получит 301, он автоматически перепрыгнет. Если поисковая система обнаружит ошибку 301 при сканировании, она изменит доменное имя ранее включенной веб-страницы через несколько дней.

Есть и другой сценарий, если вы хотите автоматически переходить на https при доступе к http, вы также можете использовать 301, потому что если вы прямо вводите доменное имя в адресной строке браузера и нажимаете Enter, впереди нет https, то это протокол http по умолчанию, на этот раз мы хотим, чтобы пользователи могли получить доступ к защищенному https, а не http, поэтому для перенаправления вы также можете использовать 301, например:

server {
    listen       80; 
    server_name  www.rrfed.com;

    if ($scheme != "https") {
         return 301 https://$host$request_uri;
    }   
}

2. 302 Найденный ресурс временно передан

Многие короткие ссылки переходят на длинные с использованием кода 302, как показано на следующем рисунке:

3. 304 Не изменено

При локальном использовании webpack-dev-server для разработки, если js/css не изменены, запрос локальных файлов js/css вернет 304 при обновлении страницы, как показано на следующем рисунке:

Откуда сервису webpack-dev-server знать, что он не модифицировался, ведь браузер при запросе выводит тег etag, например:

W/"10e632-Oz38I6asQyS459XpsaJYkjMUoZI"

Сервис вычислит etag текущего файла, который является хеш-значением файла, а затем сравнит переданные etags, если они равны, то считается, что модификации нет, и возвращает 304. Если есть какая-либо модификация, он вернет 200 и содержимое файла, а также даст браузеру новый etag. Браузер перенесет этот новый etag со следующим запросом. Если включено отключение кеширования консоли, браузер не выведет ее даже при наличии etag. Еще одним полем для определения наличия модификации является время последнего изменения, которое основано на времени модификации файла.

4. 400 Bad Request Запрос недействителен

При отсутствии необходимых параметров и неправильном формате параметра бэкенд обычно возвращает 400, как показано на следующем рисунке:

И принесите оперативную информацию:

{"message":"opportunityId type mismatch required type 'long' "}

Через 400 вы можете узнать, что параметры запроса неверны в сочетании с подсказкой, что указывает на то, что вам нужно передать число вместо строки.

5. 403 Запрещенный отказ в обслуживании

Сервис может понять ваш запрос, в том числе правильно передать параметры, но отказывается предоставлять услугу. Например, сервис разрешает прямой доступ к статическим файлам:

Но доступ к определенному каталогу запрещен:

В противном случае другие люди будут иметь беспрепятственный доступ к файлам на вашем сервере.

Разница между 403 и 401 в том, что 401 — это не аутентификация, не проверка входа и другие ошибки.

6. 500 Внутренняя ошибка сервера

Если в бизнес-коде возникнет исключение, которое не будет перехвачено и перехвачено tomcat, будет возвращена ошибка 500:

Например, длина поля базы данных ограничена 30 символами. Если запись из 31 символа будет вставлена ​​напрямую без оценки, база данных выдаст исключение. Если исключение не перехвачено и не обработано, оно сразу вернет 500.

Когда сервис полностью отключен и возврата нет, то это 502.

7. 502 Bad Gateway Ошибка шлюза

Как показано ниже:

Эта ситуация связана с тем, что nginx получил запрос, но запрос не был вызван, это может быть связано с тем, что бизнес-сервис зависает, или номер порта, вызванный в прошлом, неверен:

server {
    location / {
        # webpack的服务
        proxy_pass https://127.0.0.1:7071;
    }
}

nginx вернул 502.

8. 504 Время ожидания шлюза истекло.

Обычно это связано с тем, что служба обрабатывает запрос слишком долго, что приводит к тайм-ауту.Например, максимальное время обработки ответа на запрос службы PHP по умолчанию составляет 30 с. Если оно превышает 30 с, он зависает и возвращает 504, как показано на следующем рисунке:

Такая ситуация может быть из-за того, что сервис также запрашивает сторонние сервисы, а обработка стороннего сервиса занимает много времени и не возвращается, например, при отправке Push в FCM, если слишком много подписок, которые нужно отправить в запросе. Если вы это сделаете, обработка часто займет много времени, что приведет к ошибке 504.

9. Преобразование протокола 101

Вебсокет обновляется с http, перед установкой соединения необходимо обновить протокол через http:

Существует также 600, 600 — это менее часто используемый код состояния, указывающий, что сервер не вернул заголовок ответа, а только вернул содержимое объекта.

Эти коды состояния на самом деле являются числами и могут быть возвращены произвольно, но лучше всего возвращать соответствующие коды состояния в соответствии с положениями http. Если возвращается код состояния HTTP, начинающийся с 4, 5 или 6, браузер напечатает ошибку и посчитает, что текущий запрос не выполнен.


Мы не сказали, как судить об успешности запроса, как показано в следующем коде:

xhr.open("POST", UPLOAD_URL);
xhr.onreadystatechange = function() {
    // readyState为4表示请求完成
    if (this.readyState === 4){
        if (this.status === 200) {
            let response = JSON.parse(this.responseText);
            if (!response.status || response.status.code !== 0) {
                // 失败     
                callback.failed && callback.failed();
            } else {    
                // 成功     
                callback.success(response.data.url);
            }           
        } else if (this.status >= 400 || this.status === 0) {
            // 失败     
            callback.failed && callback.failed();
        // 正常不应该返回20几的状态码,这种情况也认为是失败
        } else {    
            callback.failed && callback.failed();
        }
    }
};
xhr.send(formData);

Тут возникает вопрос, если возвращенный код состояния является перенаправлением, начинающимся с 3, нужно ли мне самому отправлять еще один запрос?

Практика показала, что нет необходимости, браузер будет автоматически перенаправлять, как показано на следующем рисунке:


Наконец, в этой статье упоминаются три часто используемые кодировки запросов, а именно application/www-x-form-urlencoded, application/json, multipart/form-data, первая из которых является наиболее часто используемой, подходит для GET/POST и т. д. , второй формат данных, обычно используемый в ответах на запросы, и третий, обычно используемый для загрузки файлов. Затем сравниваются POST и GET.Хотя данные запроса обоих находятся в сообщении http, но расположение отличается, учитывая сценарии использования, такие как пользователи и поисковые системы, POST все еще более безопасен, чем GET. Наконец, я рассказал о нескольких часто используемых кодах состояния HTTP и привел несколько практических примеров, чтобы углубить свое впечатление и понимание.