Thêm tính năng hỗ trợ CORS vào một proxy API

Bạn đang xem tài liệu về Apigee Edge.
Truy cập vào tài liệu Apigee X.
thông tin

CORS (Chia sẻ tài nguyên khác nguồn gốc) là một cơ chế tiêu chuẩn cho phép các lệnh gọi XMLHttpRequest (XHR) của JavaScript được thực thi trong một trang web để tương tác với các tài nguyên từ các miền khác nguồn gốc. CORS là một giải pháp thường được triển khai cho "chính sách cùng nguồn" do tất cả các trình duyệt thực thi. Ví dụ: nếu bạn thực hiện một lệnh gọi XHR đến Twitter API từ mã JavaScript đang thực thi trong trình duyệt, thì lệnh gọi sẽ không thành công. Điều này là do miền phân phát trang cho trình duyệt của bạn không giống với miền phân phát Twitter API. CORS cung cấp một giải pháp cho vấn đề này bằng cách cho phép các máy chủ "chọn sử dụng" nếu muốn cung cấp tính năng chia sẻ tài nguyên trên nhiều nguồn gốc.

Video: Xem video ngắn này để tìm hiểu cách bật CORS trên một proxy API.

Trường hợp sử dụng điển hình cho CORS

Đoạn mã JQuery sau đây gọi một dịch vụ đích giả định. Nếu được thực thi trong ngữ cảnh của một trình duyệt (một trang web), lệnh gọi sẽ không thành công do chính sách cùng nguồn gốc:

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

Một giải pháp cho vấn đề này là tạo một proxy API Apigee gọi API dịch vụ ở phần phụ trợ. Hãy nhớ rằng Edge nằm giữa ứng dụng (trong trường hợp này là một trình duyệt) và API phụ trợ (dịch vụ). Vì proxy API thực thi trên máy chủ chứ không phải trong trình duyệt, nên thể gọi dịch vụ thành công. Sau đó, tất cả những gì bạn cần làm là đính kèm tiêu đề CORS vào phản hồi TargetEndpoint. Miễn là trình duyệt hỗ trợ CORS, các tiêu đề này sẽ báo hiệu cho trình trình duyệt rằng trình duyệt có thể "nới lỏng" chính sách cùng nguồn gốc, cho phép lệnh gọi API khác nguồn gốc thành công.

Sau khi tạo proxy có hỗ trợ CORS, bạn có thể gọi URL proxy API thay vì dịch vụ phụ trợ trong mã phía máy khách. Ví dụ:

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

Đính kèm chính sách Thêm CORS vào một proxy API mới

Bạn có thể thêm tính năng hỗ trợ CORS vào một proxy API bằng cách đính kèm chính sách "Thêm CORS" vào proxy API khi tạo proxy đó. Để thêm chính sách này, hãy chọn hộp đánh dấu Thêm tiêu đề CORS trong trang Bảo mật của trình hướng dẫn Tạo một proxy.

Khi bạn chọn hộp đánh dấu này, một chính sách có tên là Thêm CORS sẽ tự động được thêm vào hệ thống và được đính kèm vào luồng xử lý trước phản hồi TargetEndpoint, như minh hoạ trong hình sau:

Thêm chính sách CORS vào trình điều hướng trong mục Policies (Chính sách) và được đính kèm vào TargetEndpoint response preflow (Luồng phản hồi TargetEndpoint trước) trong ngăn bên phải

Chính sách Thêm CORS được triển khai dưới dạng một chính sách AssignMessage, chính sách này sẽ thêm các tiêu đề thích hợp vào phản hồi. Về cơ bản, các tiêu đề này cho trình duyệt biết những nguồn gốc mà trình duyệt sẽ chia sẻ tài nguyên, những phương thức mà trình duyệt chấp nhận, v.v. Bạn có thể đọc thêm về các tiêu đề CORS này trong Đề xuất của W3C về việc chia sẻ tài nguyên trên nhiều nguồn gốc.

Bạn nên sửa đổi chính sách như sau:

  • Thêm tiêu đề content-typeauthorization (bắt buộc để hỗ trợ xác thực cơ bản hoặc OAuth2) vào tiêu đề Access-Control-Allow-Headers, như minh hoạ trong đoạn mã dưới đây.
  • Đối với phương thức xác thực OAuth2, bạn có thể cần thực hiện các bước để khắc phục hành vi không tuân thủ RFC.
  • Bạn nên dùng <Set> để đặt tiêu đề CORS thay vì <Add>, như minh hoạ trong đoạn trích bên dưới. Khi sử dụng <Add>, nếu tiêu đề Access-Control-Allow-Origin đã tồn tại, bạn sẽ nhận được lỗi sau:

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    Để biết thêm thông tin, hãy xem Lỗi CORS : tiêu đề chứa nhiều giá trị "*, *", nhưng chỉ được phép có một giá trị.

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Thêm tiêu đề CORS vào một proxy hiện có

Bạn cần tạo thủ công một chính sách Assign Message mới và sao chép mã cho chính sách Add CORS được liệt kê trong phần trước vào chính sách đó. Sau đó, hãy đính kèm chính sách vào quy trình trước phản hồi của TargetEndpoint trong API proxy. Bạn có thể sửa đổi các giá trị tiêu đề nếu cần. Để biết thêm thông tin về cách tạo và đính kèm chính sách, hãy xem bài viết Chính sách là gì?.

Xử lý các yêu cầu preflight CORS

Yêu cầu preflight CORS đề cập đến việc gửi một yêu cầu đến máy chủ để xác minh xem máy chủ đó có hỗ trợ CORS hay không. Các phản hồi điển hình trước khi kiểm tra bao gồm những nguồn gốc mà máy chủ sẽ chấp nhận các yêu cầu CORS, danh sách các phương thức HTTP được hỗ trợ cho các yêu cầu CORS, các tiêu đề có thể được dùng làm một phần của yêu cầu tài nguyên, thời gian tối đa phản hồi trước khi kiểm tra sẽ được lưu vào bộ nhớ đệm và các phản hồi khác. Nếu dịch vụ không cho biết có hỗ trợ CORS hay không muốn chấp nhận các yêu cầu khác nguồn gốc từ nguồn gốc của ứng dụng, thì chính sách khác nguồn gốc của trình duyệt sẽ được thực thi và mọi yêu cầu khác miền được thực hiện từ ứng dụng để tương tác với các tài nguyên được lưu trữ trên máy chủ đó sẽ không thành công.

Thông thường, các yêu cầu kiểm tra CORS được thực hiện bằng phương thức HTTP OPTIONS. Khi nhận được một yêu cầu OPTIONS, máy chủ hỗ trợ CORS sẽ trả về một nhóm tiêu đề CORS cho máy khách để cho biết mức độ hỗ trợ CORS của máy chủ. Do quá trình bắt tay này, ứng dụng biết những gì được phép yêu cầu từ miền không phải miền gốc.

Để biết thêm thông tin về yêu cầu kiểm tra trước, hãy tham khảo Đề xuất của W3C về việc chia sẻ tài nguyên trên nhiều nguồn. Ngoài ra, còn có rất nhiều blog và bài viết về CORS mà bạn có thể tham khảo.

Apigee không có sẵn giải pháp kiểm tra CORS trước khi thực hiện, nhưng bạn có thể triển khai giải pháp này như mô tả trong phần này. Mục tiêu là để proxy đánh giá một yêu cầu OPTIONS trong một quy trình có điều kiện. Sau đó, proxy có thể gửi một phản hồi thích hợp trở lại cho máy khách.

Hãy xem một quy trình mẫu, sau đó thảo luận về các phần xử lý yêu cầu kiểm tra trước khi bay:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

Sau đây là các phần chính của ProxyEndpoint này:

  • Một RouteRule được tạo cho đích đến NULL với một điều kiện cho yêu cầu OPTIONS. Xin lưu ý rằng không có TargetEndpoint nào được chỉ định. Nếu nhận được yêu cầu OPTIONS và tiêu đề yêu cầu Origin và Access-Control-Request-Method không có giá trị rỗng, thì proxy sẽ trả về ngay các tiêu đề CORS trong phản hồi cho máy khách (bỏ qua mục tiêu "phụ trợ" mặc định thực tế). Để biết thông tin chi tiết về các điều kiện của luồng và RouteRule, hãy xem phần Điều kiện có biến luồng.

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
  • Một quy trình OptionsPreFlight được tạo để thêm chính sách Add CORS (Thêm CORS), chứa các tiêu đề CORS, vào quy trình nếu nhận được yêu cầu OPTIONS và tiêu đề yêu cầu Origin (Nguồn gốc) và Access-Control-Request-Method (Access-Control-Request-Method) không có giá trị rỗng.

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>

Sử dụng giải pháp CORS mẫu

Bạn có thể xem một giải pháp CORS mẫu (được triển khai dưới dạng một luồng dùng chung) trên GitHub. Nhập gói luồng dùng chung vào môi trường của bạn và đính kèm gói đó bằng cách sử dụng các lệnh gọi luồng hoặc trực tiếp vào các luồng proxy API. Để biết thông tin chi tiết, hãy xem tệp CORS-Shared-FLow README được cung cấp cùng với mẫu.