この記事では主に、CORS を使用してクロスドメイン Ajax リクエストを完了するための Koa2 フレームワークを紹介し、参考として提供します。
クロスドメイン 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 であるかどうかによって決まります。リクエストが正常に応答できるかどうか。
サーバー上でこの 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 の下に 2 つの Cookie を設定します。間違って Cookie を中国語に設定しないように注意してください (中国語に設定したところ、エラーが報告されました。エラーの原因は長い間見つかりませんでした)
次に、2 つのステップを実行する必要があります。最初の手順では、応答ヘッダー 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(); });
这样就减少了多余的响应头。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がKoa2 フレームワークを通じて CORS を使用してクロスドメイン Ajax リクエストを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。