今回は、Koa2 フレームワークの CORS を使用してクロスドメイン Ajax リクエストを完了する方法を説明します。Koa2 フレームワークの CORS を使用してクロスドメイン Ajax リクエストを完了するときに注意すべき注意事項とは何ですか。実際のケースを見てみましょう。
クロスドメイン Ajax リクエストを実装するには多くの方法がありますが、その 1 つは CORS を使用することであり、この方法の鍵はサーバー側で設定することです。
この記事では、通常のクロスドメイン Ajax 応答を完了できる最も基本的な設定のみを説明します (詳細な設定の方法はわかりません)。
CORS はリクエストを単純なリクエストと単純でないリクエストに分割します。単純に、追加のリクエスト ヘッダーのない get リクエストと post リクエスト と考えることができます。post リクエストの場合、リクエストの形式は application/json にすることはできません。 (私はこの分野について深く理解していないため。間違いがあれば、誰かが間違いを指摘し、修正を提案してくれることを願っています)。残りの put リクエストと post リクエスト、Content-Type application/json を含むリクエスト、およびカスタム リクエスト ヘッダーを含むリクエストは、単純ではないリクエストです。
単純なリクエストの構成は非常に簡単で、目的を達成するために応答を完了する必要がある場合は、応答ヘッダーで Access-Control-Allow-Origin を構成するだけです。
http://localhost:3000 ドメイン名の下にある http://127.0.0.1:3001 ドメイン名にアクセスしたい場合。次の設定を行うことができます:
app.use(async (ctx, next) => { ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000'); await next(); });
次に、ajax を使用して post リクエストなどの単純なリクエストを開始すると、サーバーから正しい応答を簡単に取得できます。
実験用コードは以下のとおりです:
$.ajax({ type: 'post', url: 'http://127.0.0.1:3001/async-post' }).done(data => { console.log(data); })
サーバー側コード:
router.post('/async-post',async ctx => { ctx.body = { code: "1", msg: "succ" } });
これにより、正しい応答情報を取得できます。
このときのリクエストとレスポンスのヘッダー情報を見ると、リクエストヘッダーには追加のorigin(リクエストのURLアドレスのリファラーもあります)があり、レスポンスヘッダーには追加の Access-Control-Allow-Origin。
これで単純なリクエストを送信できるようになりましたが、単純でないリクエストを送信するには他の設定が必要です。
初めて非単純なリクエストが発行されると、実際には 2 つのリクエストが発行されます。最初のリクエストは、このリクエストの リクエストメソッド が渡されるかどうかによってこのタイプが決まります。単純でないリクエストに正常に応答できるかどうか。
サーバー上でこの OPTIONS タイプのリクエストを照合するには、この事前チェックを通過できるように、照合して応答を返すミドルウェアを作成する必要があります。
app.use(async (ctx, next) => { if (ctx.method === 'OPTIONS') { ctx.body = ''; } await next(); });
このようにして、OPTIONS リクエストを渡すことができます。
プリフライトリクエストのリクエストヘッダーを確認すると、さらに 2 つのリクエストヘッダーがあることがわかります。
Access-Control-Request-Method: PUT Origin: http://localhost:3000
これら 2 つのヘッダー情報を介してサーバーとネゴシエートし、サーバーの応答条件を満たしているかどうかを確認します。
理解するのは簡単ですが、リクエストヘッダーにはさらに 2 つの情報が含まれているため、レスポンスヘッダーには当然、対応する 2 つの情報が含まれます。 2 つの情報は次のとおりです。
Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: PUT,DELETE,POST,GET
最初の情報は同じです。が原点であるため、通過します。 2 番目の情報は Access-Controll-Request-Method に対応し、サーバーが許可する応答メソッドにリクエスト メソッドが含まれている場合、この情報も渡されます。両方の制約が満たされるため、リクエストは正常に開始されます。
ここまでは、事前チェックのみを完了し、実際のリクエストを送信しないことと同じです。
もちろん、実際のリクエストもレスポンスを正常に受信しており、レスポンスヘッダーは次のとおりです(重要でない部分は省略しています)
Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: PUT,DELETE,POST,GET
リクエストヘッダーは次のとおりです:
Origin: http://localhost:3000
これは非常に明白であり、レスポンスヘッダー情報は次のように設定されています私たちはサーバー上にいます、そうです。
クライアントは、Access-Control-Request-Method リクエスト ヘッダーを送信する必要はありません。これは、直前に事前チェックされているためです。
この例のコードは次のとおりです:
$.ajax({ type: 'put', url: 'http://127.0.0.1:3001/put' }).done(data => { console.log(data); });
サーバー コード:
app.use(async (ctx, next) => { ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000'); ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET'); await next(); });
この時点で、クロスドメイン Ajax 応答を正しく実行するための基本構成が完了しました。さらに構成できるものがいくつかあります。
たとえば、これまでのところ、すべての非単純リクエストは実際に 2 つのリクエスト (プリフライト用と実際のリクエスト用) を発行するため、パフォーマンスの低下が発生します。プリフライトリクエストを送信しないようにするには、次の応答ヘッダーを設定できます。
Access-Control-Max-Age: 86400
这个响应头的意义在于,设置一个相对时间,在该非简单请求在服务器端通过检验的那一刻起,当流逝的时间的毫秒数不足Access-Control-Max-Age时,就不需要再进行预检,可以直接发送一次请求。
当然,简单请求时没有预检的,因此这条代码对简单请求没有意义。
目前代码如下:
app.use(async (ctx, next) => { ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000'); ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET'); ctx.set('Access-Control-Max-Age', 3600 * 24); await next(); });
到现在为止,可以对跨域ajax请求进行响应了,但是该域下的cookie不会被携带在请求头中。如果想要带着cookie到服务器,并且允许服务器对cookie进一步设置,还需要进行进一步的配置。
为了便于后续的检测,我们预先在http://127.0.0.1:3001这个域名下设置两个cookie。注意不要错误把cookie设置成中文(刚才我就设置成了中文,结果报错,半天没找到出错原因)
然后我们要做两步,第一步设置响应头Access-Control-Allow-Credentials为true,然后在客户端设置xhr对象的withCredentials属性为true。
客户端代码如下:
$.ajax({ type: 'put', url: 'http://127.0.0.1:3001/put', data: { name: '黄天浩', age: 20 }, xhrFields: { withCredentials: true } }).done(data => { console.log(data); });
服务端如下:
app.use(async (ctx, next) => { ctx.set('Access-Control-Allow-Origin', 'http://localhost:3000'); ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET'); ctx.set('Access-Control-Allow-Credentials', true); await next(); });
这时就可以带着cookie到服务器了,并且服务器也可以对cookie进行改动。但是cookie仍是http://127.0.0.1:3001域名下的cookie,无论怎么操作都在该域名下,无法访问其他域名下的cookie。
现在为止CORS的基本功能已经都提到过了。
一开始我不知道怎么给Access-Control-Allow-Origin,后来经人提醒,发现可以写一个白名单数组,然后每次接到请求时判断origin是否在白名单数组中,然后动态的设置Access-Control-Allow-Origin,代码如下:
app.use(async (ctx, next) => { if (ctx.request.header.origin !== ctx.origin && whiteList.includes(ctx.request.header.origin)) { ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin); ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET'); ctx.set('Access-Control-Allow-Credentials', true); ctx.set('Access-Control-Max-Age', 3600 * 24); } await next(); });
这样就可以不用*通配符也可匹配多个origin了。
注意:ctx.origin与ctx.request.header.origin不同,ctx.origin是本服务器的域名,ctx.request.header.origin是发送请求的请求头部的origin,二者不要混淆。
最后,我们再稍微调整一下自定义的中间件的结构,防止每次请求都返回Access-Control-Allow-Methods以及Access-Control-Max-Age,这两个响应头其实是没有必要每次都返回的,只是第一次有预检的时候返回就可以了。
调整后顺序如下:
app.use(async (ctx, next) => { if (ctx.request.header.origin !== ctx.origin && whiteList.includes(ctx.request.header.origin)) { ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin); ctx.set('Access-Control-Allow-Credentials', true); } await next(); }); app.use(async (ctx, next) => { if (ctx.method === 'OPTIONS') { ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET'); ctx.set('Access-Control-Max-Age', 3600 * 24); ctx.body = ''; } await next(); });
这样就减少了多余的响应头。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がKoa2 フレームワークの CORS を使用してクロスドメイン Ajax リクエストを完了する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。