2012-02-23 35 views
6

Sử dụng Java, tôi muốn loại bỏ định danh đoạn và thực hiện một số chuẩn hóa đơn giản (ví dụ: lược đồ chữ thường, máy chủ) của một tập hợp URI đa dạng. URI đầu vào và đầu ra phải tương đương với một nghĩa HTTP chung.Chuẩn hóa các chuỗi URI được mã hóa trong Java

Thông thường, điều này phải đơn giản. Tuy nhiên, đối với các URI như http://blah.org/A_%28Secret%29.xml#blah, phần trăm mã hóa (Secret), hành vi của java.util.URI làm cho cuộc sống trở nên khó khăn.

Phương pháp chuẩn hóa phải trả lại http://blah.org/A_%28Secret%29.xml vì các URI http://blah.org/A_%28Secret%29.xmlhttp://blah.org/A_(Secret).xml không tương đương với cách diễn giải [§2.2; RFC3968]

Vì vậy, chúng ta có hai phương pháp bình thường như sau:

URI u = new URI("http://blah.org/A_%28Secret%29.xml#blah"); 
System.out.println(u); 
     // prints "http://blah.org/A_%28Secret%29.xml#blah" 

String path1 = u.getPath();  //gives "A_(Secret).xml" 
String path2 = u.getRawPath(); //gives "A_%28Secret%29.xml" 


//NORMALISE METHOD 1 
URI norm1 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(), 
         u.getHost().toLowerCase(), u.getPort(), path1, 
         u.getQuery(), null); 
System.out.println(norm1); 
// prints "http://blah.org/A_(Secret).xml" 

//NORMALISE METHOD 2 
URI norm2 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(), 
         u.getHost().toLowerCase(), u.getPort(), path2, 
         u.getQuery(), null); 
System.out.println(norm2); 
// prints "http://blah.org/A_%2528Secret%2529.xml" 

Như chúng ta thấy, các URI được phân tích và xây dựng lại mà không có sự nhận dạng mảnh.

Tuy nhiên, đối với phương pháp 1, u.getPath() trả về URI chưa mã hóa, thay đổi URI cuối cùng.

Đối với phương pháp 2, u.getRawPath() trả lại đường dẫn ban đầu, nhưng khi được chuyển đến hàm tạo URI, Java quyết định thêm mã hóa kép.

Điều này giống như bẫy ngón tay của người Trung Quốc.

câu hỏi Vì vậy, hai chính:

  • Tại sao java.util.URI cảm thấy cần phải chơi với mã hóa?
  • Phương pháp chuẩn hóa này có thể được triển khai như thế nào mà không gặp khó khăn với mã hóa phần trăm ban đầu?

(Tôi thà không cần phải thực hiện các phương pháp phân tích cú pháp/concatenate của java.util.URI, đó là không tầm thường.)


EDIT: Dưới đây là một số thông tin thêm từ URI javadoc.

  • Các đơn lập luận constructor đòi hỏi bất kỳ ký tự bất hợp pháp trong đối số của nó phải được trích dẫn và bảo bất kỳ octet và các nhân vật khác mà có mặt đã trốn thoát.

  • Các nhà thầu đa đối số đa đối số trích dẫn các ký tự không hợp lệ theo yêu cầu của các thành phần mà chúng xuất hiện. Ký tự phần trăm ('%') luôn được trích dẫn bởi các nhà xây dựng này. Bất kỳ ký tự nào khác được giữ nguyên.

  • Các getRawUserInfo, getRawPath, getRawQuery, getRawFragment, getRawAuthority, và getRawSchemeSpecificPart phương pháp trả lại giá trị của các thành phần tương ứng của chúng ở dạng thô, mà không giải thích bất kỳ octet trốn thoát. Các chuỗi được trả về bởi các phương thức này có thể chứa cả hai octet thoát và các ký tự khác, và sẽ không chứa bất kỳ ký tự không hợp lệ nào.

  • Các getUserInfo, getPath, getQuery, getFragment, getAuthority, và getSchemeSpecificPart phương pháp giải mã bất kỳ thoát octet trong các thành phần tương ứng của họ. Các chuỗi được trả về bởi các phương thức này có thể chứa cả các ký tự khác và các ký tự không hợp lệ, và sẽ không chứa bất kỳ octet thoát nào.

  • Phương thức toString trả về chuỗi URI với tất cả các báo giá cần thiết nhưng có thể chứa các ký tự khác.

  • Phương thức toASCIIString trả về chuỗi URI được trích dẫn đầy đủ và được mã hóa không chứa bất kỳ ký tự nào khác.

Vì vậy, tôi không thể sử dụng hàm tạo đa đối số mà không bị mã hóa URL sai lệch nội bộ theo lớp URI. Pah!

+0

Trường hợp sử dụng là trình thu thập thông tin. Chúng tôi muốn lấy một tập hợp các URI được trích xuất và "chuẩn hóa" chúng thành một tập hợp nhỏ nhất có thể, vẫn đảm bảo rằng nội dung đã truy xuất được đảm bảo giống nhau.(Câu hỏi http://stackoverflow.com/questions/2993649/how-to-normalize-a-url-in-java có liên quan nhưng không giải quyết vấn đề tước ID phân mảnh, với việc thay đổi mã hóa URL.) – badroit

+1

Tôi cách xa các công cụ URI, và không chắc liệu bạn có cần nó theo cách tiêu chuẩn với API URI hay không, nhưng nếu tôi chỉ muốn có được chức năng này bằng cách nào đó, tôi sẽ 1) Lấy chuỗi con của url gốc cho đến khi sự xuất hiện đầu tiên của '#' hoặc '?' hoặc '&' vì đó là những gì thực sự tách url khỏi thông tin bổ sung hoặc 2) Cho phép URI tạo uri bình thường (ví dụ norm2) và sau đó thay thế tất cả% bằng bản gốc của một người trong chuỗi vị trí (1 của norm2 với 1 của bản gốc vv). Ofcourse này chỉ là nếu cách tiêu chuẩn là không thể sử dụng. –

Trả lời

9

java.net.URI được giới thiệu trong java 1.4 (mà đi ra ở 2002) và nó dựa trên RFC2396 mà đối xử '(' và ')' như nhân vật mà không cần thoát và ngữ nghĩa không thay đổi ngay cả khi nó được thoát, hơn nữa nó thậm chí còn nói rằng người ta không nên trốn thoát trừ khi nó là cần thiết (§2.3, RFC2396).

Nhưng RFC3986 (xuất hiện vào năm 2005) đã thay đổi điều này và tôi đoán các nhà phát triển JDK quyết định không thay đổi hành vi của java.net.URI để tương thích với mã hiện có.

Bằng cách googling ngẫu nhiên, tôi thấy Jena IRI có vẻ tốt.

public class IRITest { 
public static void main(String[] args) { 
    IRIFactory factory = IRIFactory.uriImplementation(); 
    IRI iri = factory.construct("http://blah.org/A_%28Secret%29.xml#blah"); 
    ArrayList<String> a = new ArrayList<String>(); 
    a.add(iri.getScheme()); 
    a.add(iri.getRawUserinfo()); 
    a.add(iri.getRawHost()); 
    a.add(iri.getRawPath()); 
    a.add(iri.getRawQuery()); 
    a.add(iri.getRawFragment()); 
    IRI iri2 = factory.construct("http://blah.org/A_(Secret).xml#blah"); 
    ArrayList<String> b = new ArrayList<String>(); 
    b.add(iri2.getScheme()); 
    b.add(iri2.getRawUserinfo()); 
    b.add(iri2.getRawHost()); 
    b.add(iri2.getRawPath()); 
    b.add(iri2.getRawQuery()); 
    b.add(iri2.getRawFragment()); 

    System.out.println(a); 
    //[http, null, blah.org, /A_%28Secret%29.xml, null, blah] 
    System.out.println(b); 
    //[http, null, blah.org, /A_(Secret).xml, null, blah] 
} 
} 
4

Lưu ý đoạn này ở cuối [§2.2; RFC3968]

URI ứng dụng sản xuất nên phần trăm mã hóa octet dữ liệu mà tương ứng với ký tự trong tập dành riêng trừ khi những nhân vật được phép đặc biệt bởi các URI scheme để đại diện cho dữ liệu trong đó thành phần. Nếu ký tự dành riêng được tìm thấy trong thành phần URI và không có vai trò phân định nào được biết cho ký tự đó, thì phải là được hiểu là biểu thị octet dữ liệu tương ứng với mã hóa của ký tự trong US-ASCII.

Vì vậy, miễn là lược đồ là http hoặc https, mã hóa là hành vi chính xác.

Thử sử dụng phương pháp toASCIIString thay vì toString để in URI. Ví dụ:

System.put.println(norm1.toASCIIString()); 
+0

Cảm ơn thông tin! Không chắc chắn tôi đồng ý với việc giải thích của bạn về đoạn văn. Phần này: "* trừ khi các ký tự này được chương trình URI cho phép cụ thể để đại diện cho dữ liệu trong thành phần đó *" cho thấy rằng không cần thiết cho HTTP/HTTPS cho phép, ví dụ: ''() "' ký tự. Trong mọi trường hợp, câu hỏi sẽ trở thành tranh luận cho trình thu thập thông tin nếu bạn xem đoạn văn "* Phần trăm mã hóa ký tự dành riêng hoặc giải mã octet được mã hóa phần trăm tương ứng với ký tự dành riêng, sẽ thay đổi cách URI được giải thích bởi hầu hết các ứng dụng. * ". (Phương thức 'toASCIIString' không có hiệu lực ở đây.) – badroit