Antipattern: เข้าถึงส่วนหัว HTTP ที่มีหลายค่าอย่างไม่ถูกต้องในพร็อกซี API

คุณกำลังดูเอกสารประกอบของ Apigee Edge
ไปที่ เอกสารประกอบของ Apigee X
info

ส่วนหัว HTTP คือคู่ชื่อ-ค่าที่ช่วยให้แอปพลิเคชันไคลเอ็นต์และบริการแบ็กเอนด์ ส่งข้อมูลเพิ่มเติมเกี่ยวกับคำขอและการตอบกลับตามลำดับ ตัวอย่างง่ายๆ มีดังนี้

  • ส่วนหัวของคำขอการให้สิทธิ์จะส่งข้อมูลเข้าสู่ระบบของผู้ใช้ไปยังเซิร์ฟเวอร์
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • ส่วนหัว Content-Type จะระบุประเภทเนื้อหาของคำขอ/การตอบกลับที่กำลังส่ง
    Content-Type: application/json

ส่วนหัว HTTP อาจมีค่าตั้งแต่ 1 ค่าขึ้นไป ทั้งนี้ขึ้นอยู่กับ คำจำกัดความของฟิลด์ส่วนหัว ส่วนหัวที่มีหลายค่าจะมีค่าที่คั่นด้วยคอมมา ต่อไปนี้เป็นตัวอย่างส่วนหัวที่มีหลายค่า

  • Cache-Control: no-cache, no-store, must-revalidate
  • Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
  • X-Forwarded-For: 10.125.5.30, 10.125.9.125

Apigee Edge ช่วยให้นักพัฒนาแอปเข้าถึงส่วนหัวได้อย่างง่ายดายโดยใช้ ตัวแปรโฟลว์ ในนโยบาย Edge หรือโฟลว์แบบมีเงื่อนไข ต่อไปนี้เป็นรายการตัวแปรที่ใช้ เข้าถึงส่วนหัวของคำขอหรือส่วนหัวการตอบกลับที่เฉพาะเจาะจงใน Edge ได้

ตัวแปรโฟลว์

  • message.header.header-name
  • request.header.header-name
  • response.header.header-name
  • message.header.header-name.N
  • request.header.header-name.N
  • response.header.header-name.N

ออบเจ็กต์ JavaScript

  • context.proxyRequest.headers.header-name
  • context.targetRequest.headers.header-name
  • context.proxyResponse.headers.header-name
  • context.targetResponse.headers.header-name

ต่อไปนี้เป็นตัวอย่างนโยบาย AssignMessage ที่แสดงวิธีอ่านค่าส่วนหัวของคำขอและจัดเก็บไว้ในตัวแปร

<AssignMessage continueOnError="false" enabled="true" name="assign-message-default">
  <AssignVariable>
    <Name>reqUserAgent</Name>
    <Ref>request.header.User-Agent</Ref>
  </AssignVariable>
</AssignMessage>

รูปแบบที่ไม่แนะนำ

การเข้าถึงค่าของส่วนหัว HTTP ในนโยบาย Edge ในลักษณะที่แสดงผลเฉพาะค่าแรก นั้นไม่ถูกต้องและอาจทำให้เกิดปัญหาได้หากส่วนหัว HTTP ที่เฉพาะเจาะจงมีค่ามากกว่า 1 ค่า

ส่วนต่อไปนี้มีตัวอย่างการเข้าถึงส่วนหัว

ตัวอย่างที่ 1: อ่านส่วนหัว Accept ที่มีหลายค่าโดยใช้โค้ด JavaScript

สมมติว่าส่วนหัว Accept มีหลายค่าดังที่แสดงด้านล่าง

Accept: text/html, application/xhtml+xml, application/xml

ต่อไปนี้เป็นโค้ด JavaScript ที่อ่านค่าจากส่วนหัว Accept

// Read the values from Accept header
var acceptHeaderValues = context.getVariable("request.header.Accept");

โค้ด JavaScript ด้านบนจะแสดงผลเฉพาะค่าแรกจากส่วนหัว Accept เช่น text/html

ตัวอย่างที่ 2: อ่านส่วนหัว Access-Control-Allow-Headers ที่มีหลายค่าในนโยบาย AssignMessage หรือ RaiseFault

สมมติว่าส่วนหัว Access-Control-Allow-Headers มีหลายค่าดังที่แสดงด้านล่าง

Access-Control-Allow-Headers: content-type, authorization

ต่อไปนี้เป็นส่วนของโค้ดจากนโยบาย AssignMessage หรือ RaiseFault ที่ตั้งค่าส่วนหัว Access-Control-Allow-Headers

<Set>
  <Headers>
    <Header name="Access-Control-Allow-Headers">{request.header.Access-Control-Request-Headers}</Header>
  </Headers>
</Set>

โค้ดด้านบนจะตั้งค่าส่วนหัว Access-Control-Allow-Headers โดยใช้เฉพาะค่าแรกจาก ส่วนหัวของคำขอ Access-Control-Allow-Headers ซึ่งในตัวอย่างนี้คือ content-type

ผลกระทบ

  1. ในตัวอย่างทั้ง 2 ด้านบน โปรดสังเกตว่าระบบจะแสดงผลเฉพาะค่าแรกจากส่วนหัวที่มีหลายค่า หากนโยบายอื่นในโฟลว์พร็อกซี API หรือบริการแบ็กเอนด์ ใช้ค่าเหล่านี้ในภายหลังเพื่อทำหน้าที่หรือตรรกะบางอย่าง ก็อาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด
  2. เมื่อมีการเข้าถึงค่าส่วนหัวของคำขอและส่งไปยังเซิร์ฟเวอร์เป้าหมาย แบ็กเอนด์อาจประมวลผลคำขอ API อย่างไม่ถูกต้องและแสดงผลลัพธ์ที่ไม่ถูกต้อง
  3. หากแอปพลิเคชันไคลเอ็นต์ขึ้นอยู่กับค่าส่วนหัวที่เฉพาะเจาะจงจากการตอบกลับของ Edge แอปพลิเคชันดังกล่าวก็อาจประมวลผลอย่างไม่ถูกต้องและแสดงผลลัพธ์ที่ไม่ถูกต้องด้วย

แนวทางปฏิบัติแนะนำ

  1. ใช้ตัวแปรโฟลว์ในตัวที่เหมาะสม ได้แก่ request.header.header_name.values.count, request.header.header_name.N, response.header.header_name.values.count, response.header.header_name.N.

    จากนั้นทำซ้ำเพื่อดึงค่าทั้งหมดจากส่วนหัวที่เฉพาะเจาะจงในนโยบาย JavaScript หรือ Java Callout

    ตัวอย่าง: โค้ด JavaScript ตัวอย่างเพื่ออ่านส่วนหัวที่มีหลายค่า

    for (var i = 1; i <=context.getVariable('request.header.Accept.values.count'); i++)
    {
      print(context.getVariable('request.header.Accept.' + i));
    }

    เช่น application/xml;q=0.9, */*;q=0.8 จะปรากฏเป็นค่าเดียวเมื่อใช้โค้ดด้านบน

    หากต้องการแยกค่าส่วนหัวโดยใช้เซมิโคลอนเป็นตัวคั่น ให้ใช้ string.split(";") เพื่อแยกค่าเหล่านี้

  2. ใช้ฟังก์ชัน substring() กับตัวแปรโฟลว์ request.header.header_name.values ในนโยบาย RaiseFault หรือ AssignMessage เพื่ออ่านค่าทั้งหมดของส่วนหัวที่เฉพาะเจาะจง

    ตัวอย่าง: นโยบาย RaiseFault หรือ AssignMessage ตัวอย่างเพื่ออ่านส่วนหัวที่มีหลายค่า

    <Set>
      <Headers>
       <Header name="Access-Control-Allow-Headers">{substring(request.header.Access-Control-Request-Headers.values,1,-1)}</Header>
      </Headers>
    </Set>

อ่านเพิ่มเติม