В этой статье в основном представлены два междоменных метода, JSONP и CORS. В основе лежит моделирование Koa. Настоящая цель — понять весь междоменный процесс. Что касается междоменной и браузерной политики одного и того же происхождения, пожалуйста, Baidu самостоятельно.
JSONP
JSONP на самом деле является трюком, использующим возможность браузера получать доступ к междоменным данным с помощью тега src (нет междоменных проблем, таких как img, теги ссылок и т. д.).
<!DOCTYPE html>
<html>
<head>
<title>模拟JSONP跨域请求</title>
</head>
<body>
<script type="text/javascript">
var message = 'hello world';
function doSomething(data) {
// 处理返回的数据
document.write(data);
}
</script>
<script src="http://127.0.0.1:3000/jsonp?callback=doSomething&msg=message"></script>
</body>
</html>
var Koa = require("koa");
var Router = require("koa-router");
var app = new Koa();
var router = new Router();
router.get("/", (ctx, next) => {
ctx.body = "Hello World!";
});
// jsonp跨域请求
router.get("/jsonp", (ctx, next) => {
// 获取参数
const query = ctx.request.query;
ctx.body = `${query.callback}(${query.msg})`;
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
Когда внутренний запрос будет завершен, будет вызвана функция обратного вызова, будет передан соответствующий параметр сообщения и будет выполнена функция doSomething.
Преимущества и недостатки JSONP
Достоинства: хорошая совместимость
недостаток:
- JSONP поддерживает только запросы GET.
- XMLHttpRequest имеет лучший механизм обработки ошибок, чем JSONP.
CORS
MDN: совместное использование ресурсов между источниками (CORS) — это механизм, который использует дополнительные заголовки HTTP, чтобы сообщить браузерам, что веб-приложениям, работающим в одном источнике (домене), разрешен доступ к указанным ресурсам с серверов в разных источниках. Когда ресурс запрашивает ресурс из домена, протокола или порта, отличного от сервера, на котором находится сам ресурс, ресурс инициирует междоменный HTTP-запрос.
Следует отметить, что для CORS асинхронные запросы делятся на простые запросы и непростые запросы, Непростые запросы сначала инициируют предварительную проверку, которую мы называем предварительной проверкой.
простой запрос
Используйте один из следующих методов:
- GET
- HEAD
- POST
Заголовки HTTP-запросов ограничены следующим:
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
Значение Content-Type ограничено одним из следующих трех:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
Это выглядит очень сложно, как мы это понимаем? По сути, простой запрос — это запрос, который может сделать нативная форма HTML-формы, не полагаясь на скрипты.Давайте посмотрим на атрибут enctype формы:
enctype
- application/x-www-form-urlencoded: значение по умолчанию, если атрибут не указан.
- multipart/form-data: когда форма содержит type=fileэлемент, использующий это значение.
- text/plain
На самом деле, простые запросы также можно разделить на запросы в нативной форме (без использования скриптов) и простые запросы, инициированные через скрипты.Давайте сначала рассмотрим запросы в нативной форме:
<!DOCTYPE html>
<html>
<head>
<title>CORS-form</title>
</head>
<body>
<form
action="http://127.0.0.1:3000/cors/form-request"
method="get"
class="form-example"
>
<div class="form-example">
<label for="name">Enter your name: </label>
<input type="text" name="name" id="name" />
</div>
<div class="form-example">
<label for="email">Enter your email: </label>
<input type="email" name="email" id="email" />
</div>
<div class="form-example">
<input type="submit" value="Subscribe!" />
</div>
</form>
</body>
</html>
var Koa = require("koa");
var Router = require("koa-router");
var app = new Koa();
var router = new Router();
router.get("/", (ctx, next) => {
ctx.body = "Hello World!";
});
// CORS原生表单请求
router.get("/cors/form-request", (ctx, next) => {
console.log("form request");
ctx.body = "Hello easy form!";
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
Мы видим, что в нативных формах нет междоменной ситуации, давайте посмотрим на использование скриптов для имитации отправки формы:
<!DOCTYPE html>
<html>
<head>
<title>cors</title>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<script>
function easyRequest() {
axios({
method: "get",
url: "http://127.0.0.1:3000/cors/form-request",
params: {
username: "test",
email: "test.com",
},
headers: {
"Content-type": "application/x-www-form-urlencoded",
},
}).then((res) => {
document.write(res.data);
});
}
easyRequest();
</script>
</body>
</html>
Мы обнаружим, что тег Origin добавлен в заголовок Request Headers. Поле Origin используется для указания источника (протокол + доменное имя + порт), из которого исходит этот запрос. На основании этого значения сервер решит, соглашаться ли на запрос. Если источник, указанный Origin, не входит в разрешенную область, сервер вернет обычный HTTP-ответ. Браузер обнаруживает, что информация заголовка этого ответа не содержит поля Access-Control-Allow-Origin, и знает, что произошла ошибка, поэтому выдает ошибку, которая перехватывается функцией обратного вызова onerror XMLHttpRequest. (Обратите внимание, что этот тип ошибки не может быть идентифицирован по коду состояния, поскольку ответ HTTP может иметь код состояния 200.)
не простой запрос
Теперь давайте посмотрим на не простой запрос:
<!DOCTYPE html>
<html>
<head>
<title>cors</title>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<script>
function request() {
axios({
method: 'put',
url: 'http://127.0.0.1:3000/cors/request',
params: {
msg: 'hello cors'
},
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
}).then((res)=> {
document.write(res.data)
})
}
setInterval(request, 5000);
</script>
</body>
</html>
var Koa = require("koa");
var Router = require("koa-router");
var app = new Koa();
var router = new Router();
// 设置CORS
app.use(async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", "*");
ctx.set("Access-Control-Allow-Methods", "GET,POST,PUT");
ctx.set("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
ctx.set("Access-Control-Max-Age", 10);
if (ctx.request.method == "OPTIONS") {
ctx.body = 200;
} else {
await next();
}
});
// CORS跨域非简单请求
router.put("/cors/request", (ctx, next) => {
ctx.body = "Hello world!";
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
Мы найдем еще один запрос OPTIONS, который мы называем предварительным запросом. Браузер спросит у сервера, находится ли доменное имя текущей страницы в списке разрешенных сервером, и какие HTTP-глаголы и поля заголовка можно использовать. Браузер выдаст формальный запрос XMLHttpRequest только после положительного ответа, в противном случае будет сообщено об ошибке.
Если доменное имя, указанное Origin, находится в допустимом диапазоне, ответ, возвращаемый сервером, будет содержать несколько дополнительных полей заголовка.
- Access-Control-Allow-Headers: поля заголовка используются в ответ на предварительные запросы. Он определяет поля заголовка, которые разрешено использовать в фактическом запросе.
- Access-Control-Allow-Methods: Поле заголовка используется в ответ на предварительные запросы. Он указывает методы HTTP, разрешенные для фактического запроса.
- Access-Control-Allow-Origin: значение параметра указывает URI внешнего домена, которому разрешен доступ к ресурсу.
После того, как сервер передаст «предварительный» запрос, каждый последующий обычный запрос CORS от браузера будет таким же, как простой запрос, и будет поле заголовка Origin. Мы также можем контролировать своевременность «предварительных» запросов, установив Access-Control-Max-Age.
На этом кроссдоменная практика закончилась, давайте подумаем, почему браузер различает простые запросы и непростые запросы? Давайте посмотрим, что объяснил Учитель Хэ Шицзюнь:
Этот механизм предварительной проверки может быть ограничен только непростыми запросами. При обработке простых запросов, если сервер не намерен принимать запросы из разных источников, он не может полагаться на механизм предварительной проверки CORS. Поскольку обычные формы также могут инициировать простые запросы без CORS, по умолчанию невозможно запретить перекрестное происхождение.
В этом случае бессмысленно просто запрашивать отправку префлайта, даже если он будет отправлен на сервер, он не будет сохранять каждый последующий расчет, вместо этого в начале есть еще один префлайт. Некоторые люди интерпретируют простые запросы без предварительной проверки как «обратно совместимые». Это тоже не может быть ошибкой. Но строго говоря, не "для обратной совместимости" выпускать нельзя. Теоретически браузеры могут по-разному обрабатывать запросы формы и запросы без формы — предварительная проверка не отправляется для традиционных отправлений формы из разных источников, чтобы поддерживать совместимость, и только запросы без формы из разных источников отправляются в предварительную проверку.
Но это не приносит большой пользы, а только усложняет ситуацию. Например, вы можете использовать скрипт для прямой отправки обычного кросс-оригинального запроса, хотя (в случае, когда сервер не имеет кросс-оригинальной обработки по умолчанию) вы не можете получить результат ответа, а ваши потребности могут быть только отправлены без возврата, например, ведение журнала. Но теперь вы не можете этого сделать, если сервер не понимает preflight.
И если вы это сделаете, сервер по умолчанию разрешит кросс-оригинальные формы.Если вы хотите контролировать кросс-оригинал, вам все равно придется выполнять логику кросс-оригинальных вычислений непосредственно в обработке ответов (как и раньше); с другой стороны, серверу необходимо увеличить поддержку ответов для предварительных запросов, выполняя аналогичную логику вычислений между источниками для управления одними и теми же запросами между источниками из неформ. У серверов обычно нет необходимости различать различия между формами и не формами, поэтому делать это просто означает бросать инженеров на стороне сервера.
Таким образом, простой запрос не отправлять предварительную проверку не из-за несовместимости, а потому, что для большинства серверных приложений бессмысленно отправлять предварительную проверку в предположении совместимости, но это усложняет проблему.
Справочная статья
nuggets.capable/post/684490… Вууху. Руан Ифэн.com/blog/2016/0…
Адрес источника
Добро пожаловать, чтобы следовать
Кодировать слова непросто, надеюсь, каждый сможет обратить внимание на волну публичных аккаунтов, учиться вместе и легко вместе.