อะไรถูกต้องและสิ่งใดไม่อยู่ในแบบสอบถาม URI


101

ความเป็นมา (คำถามต่อไป)

ฉันได้รับ Googling นี้ไปมาเพื่ออ่านคำถาม RFCs และ SO ที่พยายามจะถอดรหัสสิ่งนี้ แต่ฉันก็ยังไม่มีแจ็ค

ดังนั้นฉันเดาว่าเราแค่โหวตให้คำตอบที่ "ดีที่สุด" เท่านั้นหรือ?

โดยทั่วไปแล้วมันจะเดือดลงถึงสิ่งนี้

3.4. คอมโพเนนต์การสืบค้น

คอมโพเนนต์แบบสอบถามคือสตริงของข้อมูลที่ทรัพยากรจะตีความ

query = *uric

ภายในคอมโพเนนต์การค้นหาอักขระ ";", "/", "?", ":", "@", "&", "=", "+", "," และ "$" จะถูกสงวนไว้

สิ่งแรกที่ทำให้ฉันประหลาดใจคือ * uric ถูกกำหนดไว้เช่นนี้

uric = reserved | unreserved | escaped

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

อย่างไรก็ตามสิ่งนี้ค่อนข้างชัดเจนโดยย่อหน้าเช่น

คลาสไวยากรณ์ "สงวนไว้" ด้านบนหมายถึงอักขระที่ได้รับอนุญาตภายใน URI แต่อาจไม่ได้รับอนุญาตภายในองค์ประกอบเฉพาะของไวยากรณ์ URI ทั่วไป ใช้เป็นตัวคั่นของส่วนประกอบที่อธิบายไว้ในส่วนที่ 3

อักขระในชุด "สงวน" ไม่ได้สงวนไว้ในทุกบริบท ชุดของอักขระที่สงวนไว้ภายในคอมโพเนนต์ URI ที่กำหนดถูกกำหนดโดยคอมโพเนนต์นั้น โดยทั่วไปอักขระจะถูกสงวนไว้หากความหมายของ URI เปลี่ยนไปหากอักขระถูกแทนที่ด้วยการเข้ารหัส US-ASCII ที่หลีกเลี่ยง

ข้อความที่ตัดตอนมาล่าสุดนี้ให้ความรู้สึกค่อนข้างย้อนกลับ แต่ระบุชัดเจนว่าชุดอักขระที่สงวนไว้ขึ้นอยู่กับบริบท แต่ 3.4 ระบุว่าอักขระที่สงวนไว้ทั้งหมดจะถูกสงวนไว้ในองค์ประกอบของแบบสอบถามอย่างไรก็ตามสิ่งเดียวที่จะเปลี่ยนความหมายในที่นี้คือการหลีกเลี่ยงเครื่องหมายคำถาม (?) เนื่องจาก URI ไม่ได้กำหนดแนวคิดของสตริงการสืบค้น

ณ จุดนี้ฉันยอมแพ้ RFC ทั้งหมด แต่พบว่า RFC 1738 น่าสนใจเป็นพิเศษ

HTTP URL อยู่ในรูปแบบ:

http://<host>:<port>/<path>?<searchpart>

ภายในคอมโพเนนต์ <path> และ <searchpart> "/", ";", "?" สงวนไว้ อาจใช้อักขระ "/" ภายใน HTTP เพื่อกำหนดโครงสร้างแบบลำดับชั้น

ฉันตีความสิ่งนี้อย่างน้อยเกี่ยวกับ HTTP URL ที่ RFC 1738 แทนที่ RFC 2396 เนื่องจากแบบสอบถาม URI ไม่มีความคิดเกี่ยวกับสตริงการสืบค้นและการตีความที่สงวนไว้ไม่อนุญาตให้ฉันกำหนดสตริงการสืบค้นตามที่ฉันคุ้นเคย กำลังทำอยู่ตอนนี้

คำถาม

ทั้งหมดนี้เริ่มต้นเมื่อฉันต้องการส่งรายการตัวเลขพร้อมกับคำขอของทรัพยากรอื่น ฉันไม่ได้คิดอะไรมากและแค่ส่งผ่านเป็นค่าที่คั่นด้วยลูกน้ำ ทำให้ฉันประหลาดใจแม้ว่าจุลภาคจะถูกหลบหนี ข้อความค้นหาที่page.html?q=1,2,3เข้ารหัสกลายเป็นpage.html?q=1%2C2%2C3ใช้งานได้ แต่น่าเกลียดและไม่คาดคิด นั่นคือตอนที่ฉันเริ่มเรียนรู้ RFCs

คำถามแรกของฉันคือการเข้ารหัสลูกน้ำจำเป็นจริงหรือ?

คำตอบของฉันตาม RFC 2396: ใช่ตาม RFC 1738: ไม่ใช่

ต่อมาฉันพบโพสต์ที่เกี่ยวข้องเกี่ยวกับการส่งรายการระหว่างคำขอ ในกรณีที่แนวทาง csv ทรงตัวไม่ดี สิ่งนี้ปรากฏขึ้นแทน (ไม่เคยเห็นมาก่อน)

page.html?q=1;q=2;q=3

คำถามที่สองของฉันนี่คือ URL ที่ถูกต้องหรือไม่

คำตอบของฉันตาม RFC 2396: ไม่ตาม RFC 1738: ไม่ (สงวนไว้)

ฉันไม่มีปัญหาใด ๆ กับการส่ง csv ตราบเท่าที่เป็นตัวเลข แต่ใช่คุณจะเสี่ยงต่อการต้องเข้ารหัสและถอดรหัสค่ากลับไปกลับมาหากจำเป็นต้องใช้เครื่องหมายจุลภาคกะทันหันสำหรับอย่างอื่น อย่างไรก็ตามฉันลองใช้สตริงแบบสอบถามกึ่งโคลอนด้วย ASP.NET และผลลัพธ์ก็ไม่ใช่สิ่งที่ฉันคาดหวัง

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

ฉันไม่เห็นว่าสิ่งนี้แตกต่างจากแนวทาง csv อย่างไรเมื่อฉันขอ "a" ฉันได้รับสตริงที่มีเครื่องหมายจุลภาค ASP.NET ไม่ใช่การใช้งานอ้างอิงอย่างแน่นอน แต่ก็ยังไม่ทำให้ฉันผิดหวัง

แต่ที่สำคัญที่สุด - คำถามที่สามของฉัน - ข้อกำหนดสำหรับสิ่งนี้อยู่ที่ไหน? และคุณจะทำอะไรหรือไม่ทำเพื่อเรื่องนั้น?


RFC 1738 จะแทนที่ RFC 2396 ได้อย่างไรเมื่อ RFC 2396 เผยแพร่ในอีกเกือบ 4 ปีต่อมา
Matthew Flaschen

1
เกี่ยวกับ URL และสิ่งที่สมเหตุสมผลมันคือการตีความของฉันว่ามันเป็นอย่างไร (แทนที่อาจไม่ใช่คำที่ถูกต้องเนื่องจากถูกใช้ในคำศัพท์ RFC เพื่อเลิกใช้งาน RFC เก่า RFC 1738 ไม่รู้สึกว่าเลิกใช้เมื่อเป็นข้อมูลจำเพาะเพียงอย่างเดียวหากพบว่าอนุญาตให้คุณใส่สตริงข้อความค้นหาในส่วนค้นหา ของ URL)
John Leidegren

คำตอบ:


70

การที่อักขระถูกสงวนไว้ภายในส่วนประกอบ URL ทั่วไปไม่ได้หมายความว่าจะต้องมีการ Escape เมื่อปรากฏภายในคอมโพเนนต์หรือภายในข้อมูลในคอมโพเนนต์ อักขระต้องถูกกำหนดให้เป็นตัวคั่นภายในไวยากรณ์ทั่วไปหรือเฉพาะแบบแผนและลักษณะของอักขระต้องอยู่ในข้อมูล

มาตรฐานปัจจุบันสำหรับ URI ทั่วไปคือRFC 3986ซึ่งกล่าวได้ว่า:

2.2. อักขระที่สงวนไว้

URI ประกอบด้วยองค์ประกอบและองค์ประกอบย่อยที่คั่นด้วยอักขระในชุด "สงวน" อักขระเหล่านี้เรียกว่า "สงวน" เนื่องจากอาจ (หรืออาจไม่) ถูกกำหนดให้เป็นตัวคั่นโดยไวยากรณ์ทั่วไปโดยไวยากรณ์เฉพาะของแต่ละรูปแบบหรือโดยไวยากรณ์เฉพาะการนำไปใช้งานของอัลกอริทึมการอ้างอิงของ URI หากข้อมูลสำหรับคอมโพเนนต์ URI ขัดแย้งกับจุดประสงค์ของอักขระที่สงวนไว้เป็นตัวคั่น [เน้นเพิ่ม] ข้อมูลที่ขัดแย้งจะต้องเข้ารหัสเปอร์เซ็นต์ก่อนที่จะสร้าง URI

   สงวนไว้ = gen-delims / sub-delims

   gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"

   sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

3.3. องค์ประกอบเส้นทาง

[... ]
pchar = ไม่ถูกจอง / เข้ารหัส pct / ตัวคั่นย่อย / ":" / "@"
[... ]

3.4 ส่วนประกอบของแบบสอบถาม

[... ]
      แบบสอบถาม = * (pchar / "/" / "?")

ดังนั้นจึงอนุญาตให้ใช้เครื่องหมายจุลภาคอย่างชัดเจนภายในสตริงการสืบค้นและจะต้องใช้ Escape ในข้อมูลหากรูปแบบเฉพาะกำหนดให้เป็นตัวคั่น โครงร่าง HTTP ไม่ใช้เครื่องหมายจุลภาคหรือเซมิโคลอนเป็นตัวคั่นในสตริงการสืบค้นดังนั้นจึงไม่จำเป็นต้องใช้ Escape เบราว์เซอร์เป็นไปตามมาตรฐานนี้หรือไม่ก็เป็นอีกเรื่องหนึ่ง

การใช้ CSV ควรทำงานได้ดีสำหรับข้อมูลสตริงคุณเพียงแค่ต้องปฏิบัติตามข้อกำหนด CSV มาตรฐานและอ้างอิงข้อมูลหรือหลีกเลี่ยงเครื่องหมายจุลภาคด้วยแบ็กสแลช

สำหรับ RFC 2396 ยังอนุญาตให้ใช้เครื่องหมายจุลภาคที่ไม่ใช้ Escape ในสตริงการสืบค้น HTTP:

2.2. อักขระที่สงวนไว้

URI จำนวนมากมีส่วนประกอบที่ประกอบด้วยหรือคั่นด้วยอักขระพิเศษบางตัว อักขระเหล่านี้เรียกว่า "สงวน" เนื่องจากการใช้งานภายในคอมโพเนนต์ URI ถูก จำกัด ไว้ตามวัตถุประสงค์ที่สงวนไว้ หากข้อมูลสำหรับคอมโพเนนต์ URI ขัดแย้งกับวัตถุประสงค์ที่สงวนไว้ข้อมูลที่ขัดแย้งนั้นจะต้องถูกหลบหนีก่อนที่จะสร้าง URI

เนื่องจากจุลภาคไม่มีจุดประสงค์ที่สงวนไว้ภายใต้แบบแผน HTTP จึงไม่จำเป็นต้องมีการใช้ Escape ในข้อมูล หมายเหตุจาก§ 2.3 เกี่ยวกับอักขระที่สงวนไว้คืออักขระที่เปลี่ยนความหมายเมื่อมีการเข้ารหัสเปอร์เซ็นต์โดยทั่วไปเท่านั้น อักขระอาจเข้ารหัสเป็นเปอร์เซ็นต์โดยไม่เปลี่ยนความหมายสำหรับรูปแบบเฉพาะและยังคงสงวนไว้


24

เพื่อตอบว่าอะไรถูกต้องในสตริงข้อความค้นหาฉันได้ตรวจสอบว่าอักขระพิเศษใดที่ Chrome แทนที่เมื่อทำการร้องขอ:

Space -> %20
! -> !
" -> %22
# -> removed, marks the end of the query string
% -> %
& -> &
' -> %27
( -> (
) -> )
* -> *
+ -> + (this usually means blank when received at the server, so encode if necessary)
, -> ,
- -> -
. -> .
/ -> /
: -> :
; -> ;
< -> %3C
= -> =
> -> %3E
? -> ?
@ -> @
[ -> [
\ -> \
] -> ]
^ -> ^
_ -> _
` -> `
{ -> {
| -> |
} -> }
~ -> ~

Extended ASCII (like °) -> Every character from this set is encoded

หมายเหตุ: นั่นอาจไม่ได้หมายความว่าคุณไม่ควรหลีกเลี่ยงอักขระที่ไม่ได้รับการแทนที่เมื่อคุณสร้าง URI สำหรับลิงก์ ตัวอย่างเช่นมักไม่แนะนำให้ใช้~ใน URI เนื่องจากปัญหาความเข้ากันได้ แต่ยังคงเป็นอักขระที่ถูกต้อง

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

ดังนั้นเพื่อตอบสิ่งที่ควรเข้ารหัส: อักขระและอักขระไม่ถูกต้องที่คุณต้องการใช้ตามตัวอักษร แต่มีความหมายพิเศษหรืออาจทำให้เกิดปัญหาในตอนท้ายของเซิร์ฟเวอร์


คือ/programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b#1;c#2พารามิเตอร์การค้นหาที่ถูกต้อง?
Sumit Jain

@SumitJain ไม่เพราะ#ไม่สามารถปรากฏในส่วนแบบสอบถามของ URI ตามที่เป็นอยู่ คุณจะต้องเข้ารหัสเป็น%23เพื่อให้ URI /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b%231;c%232ที่ควรจะเป็น
Dai

10

เพียงแค่ใช้ ?q=1+2+3

ฉันกำลังตอบคำถามที่สี่ที่นี่ :) ที่ไม่ได้ถาม แต่ทั้งหมดเริ่มต้นด้วย: ฉันจะส่งผ่านรายการตัวเลขค่าที่คั่นด้วยเครื่องหมายจุลภาคได้อย่างไร ดูเหมือนว่าฉันวิธีที่ดีที่สุดคือเพียงแค่จะผ่านพวกเขาพื้นที่แยกออกจากกันที่ช่องว่างที่จะได้รับ URL +แบบเข้ารหัสไป ใช้งานได้ดีตราบเท่าที่คุณทราบว่าค่าในรายการไม่มีช่องว่าง (ตัวเลขบางอย่างมักจะไม่ทำ)


แม้ว่าสิ่งนี้ควรเป็นความคิดเห็น (เนื่องจากไม่ได้ตอบคำถาม) ขอบคุณ +มีเหตุผลมากขึ้นในกรณีเฉพาะที่ฉันต้องการใช้ลูกน้ำ
Gajus

6

page.html? q = 1; q = 2; q = 3

นี่คือ URL ที่ถูกต้องหรือไม่

ใช่. ;สงวนไว้ แต่ไม่ได้โดย RFC บริบทที่กำหนดองค์ประกอบนี้คือคำจำกัดความของapplication/x-www-form-urlencodedประเภทสื่อซึ่งเป็นส่วนหนึ่งของมาตรฐาน HTML (หัวข้อ17.13.4.1 ) โดยเฉพาะโน้ตลับๆที่ซ่อนอยู่ในส่วนB.2.2 :

เราขอแนะนำให้ผู้ติดตั้งเซิร์ฟเวอร์ HTTP และโดยเฉพาะอย่างยิ่งตัวดำเนินการ CGI สนับสนุนการใช้ ";" แทนที่ "&" เพื่อช่วยผู้เขียนไม่ให้มีปัญหาในการหลีกเลี่ยงอักขระ "&" ​​ในลักษณะนี้

น่าเสียดายที่เฟรมเวิร์กสคริปต์ฝั่งเซิร์ฟเวอร์ยอดนิยมจำนวนมากรวมถึง ASP.NET ไม่รองรับการใช้งานนี้


ดังนั้นในขณะที่?q=1;q=2;q=3แบบสอบถามที่ถูกต้องก็จะคลุมเครือ: บางกรอบฝั่งเซิร์ฟเวอร์จะอ่านมันจะหมายถึง{ q: '1;q=2;q=3' }อื่น ๆ { q: {'1', '2', '3'}}ที่อาจทำมันคล้ายกับ
Nas Banov

1
ใช่. และที่แย่ไปกว่านั้นตอนนี้ HTML5 ไม่รวมภาษาเกี่ยวกับ;หมายความว่า HTML4 และ HTML5 ไม่สอดคล้องกัน ฮึอันตรายของภาษาที่ไม่เป็นบรรทัดฐานในเอกสารข้อมูลจำเพาะ ...
bobince

@NasBanov และคนอื่น ๆ (เช่น PHP) จะตีความว่า{ q: 3 }
Nicholas Shanks

1
@NicholasShanks - ที่ PHP มีส่วนเกี่ยวข้องการเดิมพันทั้งหมดจะปิด! :)
Nas Banov

1

ฉันต้องการทราบว่าpage.html?q=1&q=2&q=3เป็น URL ที่ถูกต้องเช่นกัน นี่เป็นวิธีที่ถูกต้องอย่างสมบูรณ์ในการแสดงอาร์เรย์ในสตริงข้อความค้นหา เทคโนโลยีเซิร์ฟเวอร์ของคุณจะเป็นตัวกำหนดว่าจะนำเสนออย่างไร

ใน Classic ASP คุณตรวจสอบResponse.QueryString("q").Countแล้วใช้Response.QueryString("q")(0)(และ (1) และ (2))

โปรดทราบว่าคุณเห็นสิ่งนี้ใน ASP.NET ของคุณด้วย (ฉันคิดว่ามันไม่ได้ตั้งใจ แต่ดู):

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

สังเกตว่าเซมิโคลอนถูกละเว้นดังนั้นคุณจึงaกำหนดสองครั้งและคุณได้รับค่าสองครั้งโดยคั่นด้วยเครื่องหมายจุลภาค การใช้เครื่องหมายแอมเพอร์แซนด์ทั้งหมดDefault.aspx?a=1&a=2&b=1&a=3จะให้ผลaเป็น "1,2,3" แต่ฉันแน่ใจว่ามีวิธีรับแต่ละองค์ประกอบในกรณีที่องค์ประกอบนั้นมีเครื่องหมายจุลภาค เป็นเพียงคุณสมบัติเริ่มต้นของ QueryString ที่ไม่ได้จัดทำดัชนีที่เชื่อมต่อค่าย่อยเข้าด้วยกันโดยใช้ตัวคั่นลูกน้ำ


1

ฉันมีปัญหาเดียวกัน URL ที่ไฮเปอร์ลิงก์เป็น URL ของบุคคลที่สามและคาดว่าจะมีรายการพารามิเตอร์ในรูปแบบpage.html?q=1,2,3เท่านั้นและ URL page.html?q=1%2C2%2C3ไม่ทำงาน ฉันสามารถทำให้มันใช้งานได้โดยใช้จาวาสคริปต์ อาจไม่ใช่แนวทางที่ดีที่สุด แต่สามารถดูวิธีแก้ปัญหาได้ที่นี่หากช่วยเหลือใครได้บ้าง


-3

หากคุณกำลังส่งอักขระ ENCODED ไปยังไฟล์ FLASH / SWFคุณควรเข้ารหัสอักขระสองครั้ง !! (เนื่องจากโปรแกรมแยกวิเคราะห์ Flash)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.