Path.Combine cho URL?


1244

Path.Combine rất tiện dụng, nhưng có một chức năng tương tự trong khung .NET cho URL không?

Tôi đang tìm kiếm cú pháp như thế này:

Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")

sẽ trở lại:

"http://MyUrl.com/Images/Image.jpg"


14
Flurl bao gồm một Url.Combinephương pháp làm việc đó.
Todd Menier

2
Trên thực tế, // được xử lý bởi định tuyến của trang web hoặc máy chủ chứ không phải bởi trình duyệt. Nó sẽ gửi những gì bạn đặt vào thanh địa chỉ. Đó là lý do tại sao chúng tôi gặp sự cố khi chúng tôi nhập htp: // thay vì http: // Vì vậy, // có thể gây ra sự cố lớn trên một số trang web. Tôi đang viết một dll cho một trình thu thập thông tin xử lý một trang web cụ thể sẽ ném 404 nếu bạn có // trong url.
Dave Gordon

Câu trả lời:


73

một nhận xét của Todd Menier ở trên rằng Flurl bao gồm a Url.Combine.

Thêm chi tiết:

Url.Combine về cơ bản là một Path.Combine cho các URL, đảm bảo một và chỉ một ký tự phân tách giữa các phần:

var url = Url.Combine(
    "http://MyUrl.com/",
    "/too/", "/many/", "/slashes/",
    "too", "few?",
    "x=1", "y=2"
// result: "http://www.MyUrl.com/too/many/slashes/too/few?x=1&y=2" 

Nhận Flurl.Http trên NuGet :

PM> Cài đặt-Gói Flurl.Http

Hoặc có được trình tạo URL độc lập mà không có các tính năng HTTP:

PM> Cài đặt gói


4
Chà, câu hỏi này nhận được rất nhiều lưu lượng truy cập và câu trả lời với hơn 1000 lượt upvote không thực sự hoạt động trong mọi trường hợp. Nhiều năm sau, tôi thực sự sử dụng Flurl cho việc này, vì vậy tôi chấp nhận điều này. Nó dường như làm việc trong tất cả các trường hợp tôi đã gặp phải. Nếu mọi người không muốn phụ thuộc, tôi đã đăng một câu trả lời cũng hoạt động tốt.
Brian MacKay

và nếu bạn không sử dụng Flurlvà sẽ nhận được một phiên bản nhẹ, github.com/jean-lourenco/UrlCombine
lizzy91

1157

Uri có một hàm tạo nên làm điều này cho bạn: new Uri(Uri baseUri, string relativeUri)

Đây là một ví dụ:

Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");

Lưu ý từ biên tập viên: Hãy coi chừng, phương pháp này không hoạt động như mong đợi. Nó có thể cắt một phần của BaseUri trong một số trường hợp. Xem bình luận và câu trả lời khác.


369
Tôi thích việc sử dụng lớp Uri, thật không may, nó sẽ không hoạt động như Path.Combine như OP yêu cầu. Ví dụ: Uri mới (Uri mới (" test.com/mydirectory/" ), "/helloworld.aspx"). ToString () cung cấp cho bạn " test.com/helloworld.aspx "; sẽ không chính xác nếu chúng ta muốn có kết quả kiểu Path.Combine.
Bác sĩ Jones

195
Đó là tất cả trong dấu gạch chéo. Nếu phần đường dẫn tương đối bắt đầu bằng dấu gạch chéo, thì nó hoạt động như bạn mô tả. Nhưng, nếu bạn bỏ dấu gạch chéo, thì nó hoạt động theo cách bạn mong đợi (lưu ý dấu gạch chéo bị thiếu trên tham số thứ hai): Uri mới (Uri mới (" test.com/mydirectory/" ), "helloworld.aspx" ) .ToString () dẫn đến " test.com/mydirectory/helloworld.aspx ". Path.Combine hành xử tương tự. Nếu tham số đường dẫn tương đối bắt đầu bằng dấu gạch chéo, nó chỉ trả về đường dẫn tương đối và không kết hợp chúng.
Joel Beckham

70
Nếu BaseUri của bạn tình cờ là "test.com/mydirectory/mysubdirectory" thì kết quả sẽ là "test.com/mydirectory/helloworld.aspx" thay vì "test.com/mydirectory/mysubdirectory/helloworld.aspx". Sự khác biệt tinh tế là thiếu dấu gạch chéo trên tham số đầu tiên. Tôi hoàn toàn sử dụng các phương thức khung hiện có, nếu tôi phải có dấu gạch chéo ở đó thì tôi nghĩ rằng việc thực hiện partUrl1 + partUrl2 có mùi ít hơn nhiều - tôi có thể đã theo đuổi vòng gạch chéo đó trong một thời gian khá lâu lợi ích của việc không làm chuỗi concat.
Carl

64
Lý do duy nhất tôi muốn một phương thức kết hợp URI là vì vậy tôi không phải kiểm tra dấu gạch chéo. Request.ApplicationPath là '/' nếu ứng dụng của bạn ở gốc, nhưng '/ foo' nếu không.
nickd

24
Tôi -1 câu trả lời này vì điều này không trả lời vấn đề. Khi bạn muốn kết hợp url, như khi bạn muốn sử dụng Path.Combine, bạn không muốn quan tâm đến dấu vết /. và với điều này, bạn phải quan tâm. Tôi thích giải pháp của Brian MacKay hoặc mdsharpe ở trên
Baptiste Pernet

161

Đây có thể là một giải pháp đơn giản phù hợp:

public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}

7
+1: Mặc dù điều này không xử lý các đường dẫn kiểu tương đối (../../whthing.html), tôi thích cái này vì đơn giản. Tôi cũng sẽ thêm các đường viền cho ký tự '\'.
Brian MacKay

3
Xem câu trả lời của tôi cho một phiên bản đầy đủ hơn về điều này.
Brian MacKay

149

Bạn sử dụng Uri.TryCreate( ... ):

Uri result = null;

if (Uri.TryCreate(new Uri("http://msdn.microsoft.com/en-us/library/"), "/en-us/library/system.uri.trycreate.aspx", out result))
{
    Console.WriteLine(result);
}

Sẽ trở lại:

http://msdn.microsoft.com/en-us/l Library / system.uri.trycreate.aspx


53
+1: Điều này là tốt, mặc dù tôi có một vấn đề phi lý với tham số đầu ra. ;)
Brian MacKay

10
@Brian: nếu nó giúp, tất cả các phương thức TryXXX ( int.TryParse, DateTime.TryParseExact) đều có tham số đầu ra này để giúp sử dụng chúng dễ dàng hơn trong câu lệnh if. Btw, bạn không phải khởi tạo biến như Ryan đã làm trong ví dụ này.
Abel

41
Câu trả lời này gặp phải vấn đề tương tự như của Joel : tham gia test.com/mydirectory//helloworld.aspxsẽ dẫn đến kết quả test.com/helloworld.aspxdường như không phải là điều bạn muốn.
Matt Kocaj

3
Xin chào, điều này không thành công vì: if (Uri.TryCreate (Uri mới (" localhost / MyService /" ), "/ Event / someMethod? Abc = 123", out result)) {Console.WriteLine (kết quả); } Nó hiển thị cho tôi kết quả là: localhost / Event / someMethod? Abc = 123 Lưu ý: "http: //" được thay thế từ cơ sở Uri ở đây bằng stackoverflow
Faisal Mq

3
@FaisalMq Đây là hành vi chính xác, vì bạn đã truyền tham số thứ hai liên quan đến gốc. Nếu bạn đã bỏ qua thông số hàng đầu / trên tham số thứ hai, bạn sẽ nhận được kết quả mà bạn mong đợi.
Tom Lint

127

Đã có một số câu trả lời tuyệt vời ở đây. Dựa trên đề xuất mdsharpe, đây là một phương thức mở rộng có thể dễ dàng được sử dụng khi bạn muốn xử lý các trường hợp Uri:

using System;
using System.Linq;

public static class UriExtensions
{
    public static Uri Append(this Uri uri, params string[] paths)
    {
        return new Uri(paths.Aggregate(uri.AbsoluteUri, (current, path) => string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/'))));
    }
}

Và ví dụ sử dụng:

var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;

Điều này sẽ tạo ra http://example.com/subpath/part1/part2


2
Giải pháp này khiến việc viết một phương thức tĩnh UriUtils.Combine ("url cơ sở", "part1", "part2", ...) trở nên rất giống với Path.Combine (). Đẹp!
angensesen

Để hỗ trợ các URI tương đối, tôi đã phải sử dụng ToString () thay vì AbsoluteUri và UriKind.AbsoluteOrRelative trong hàm tạo Uri.
angensesen

Cảm ơn các mẹo về Uris tương đối. Thật không may, Uri không làm cho nó dễ dàng xử lý các đường dẫn tương đối vì luôn có một số lẩm bẩm với Request.ApplicationPath liên quan. Có lẽ bạn cũng có thể thử sử dụng Uri mới (HttpContext.Cản.Request.ApplicationPath) làm cơ sở và chỉ cần gọi Append trên đó? Điều này sẽ cung cấp cho bạn các đường dẫn tuyệt đối nhưng sẽ hoạt động ở bất cứ đâu trong cấu trúc trang web.
Ales Potocnik Hahonina

Tuyệt quá. Vui vì nó đã giúp người khác. Đã sử dụng điều này một thời gian và không có vấn đề gì.
Ales Potocnik Hahonina

Tôi cũng đã thêm kiểm tra nếu bất kỳ đường dẫn nào để nối thêm không phải là null hoặc chuỗi rỗng.
n.podbielski

92

Câu trả lời của Ryan Cook gần với những gì tôi theo đuổi và có thể phù hợp hơn với các nhà phát triển khác. Tuy nhiên, nó thêm http: // vào đầu chuỗi và nói chung, nó có định dạng nhiều hơn một chút so với sau.

Ngoài ra, đối với các trường hợp sử dụng của tôi, việc giải quyết các đường dẫn tương đối không quan trọng.

Câu trả lời của mdsharp cũng chứa hạt giống của một ý tưởng hay, mặc dù việc triển khai thực tế đó cần thêm một vài chi tiết để hoàn thành. Đây là một nỗ lực để bổ sung nó (và tôi đang sử dụng điều này trong sản xuất):

C #

public string UrlCombine(string url1, string url2)
{
    if (url1.Length == 0) {
        return url2;
    }

    if (url2.Length == 0) {
        return url1;
    }

    url1 = url1.TrimEnd('/', '\\');
    url2 = url2.TrimStart('/', '\\');

    return string.Format("{0}/{1}", url1, url2);
}

VB.NET

Public Function UrlCombine(ByVal url1 As String, ByVal url2 As String) As String
    If url1.Length = 0 Then
        Return url2
    End If

    If url2.Length = 0 Then
        Return url1
    End If

    url1 = url1.TrimEnd("/"c, "\"c)
    url2 = url2.TrimStart("/"c, "\"c)

    Return String.Format("{0}/{1}", url1, url2)
End Function

Mã này vượt qua bài kiểm tra sau, xảy ra trong VB:

<TestMethod()> Public Sub UrlCombineTest()
    Dim target As StringHelpers = New StringHelpers()

    Assert.IsTrue(target.UrlCombine("test1", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("/test1/", "/test2/") = "/test1/test2/")
    Assert.IsTrue(target.UrlCombine("", "/test2/") = "/test2/")
    Assert.IsTrue(target.UrlCombine("/test1/", "") = "/test1/")
End Sub

4
Nói về chi tiết: những gì về bắt buộc ArgumentNullException("url1")nếu đối số là Nothing? Xin lỗi, chỉ là kén chọn ;-). Lưu ý rằng dấu gạch chéo ngược không có gì để làm trong URI (và nếu có, nó không nên được cắt bớt), vì vậy bạn có thể xóa nó khỏi TrimXXX.
Abel

4
bạn có thể sử dụng chuỗi params [] và tham gia đệ quy chúng để cho phép nhiều hơn 2 kết hợp
Jaider

4
Tôi chắc chắn muốn điều này trong Thư viện lớp cơ sở như Path.Combine.
Uriah Blatherwick

1
@MarkHurd Tôi đã chỉnh sửa mã một lần nữa để nó có hành vi giống với C # và cũng tương tự về mặt cú pháp.
JJS

1
@BrianMacKay tôi đã phá vỡ nó, markhurd đã chỉ ra lỗi của tôi và quay lại, tôi đã cập nhật lại ... chúc mừng
JJS

36

Path.Combine không hoạt động với tôi vì có thể có các ký tự như "|" trong các đối số QueryString và do đó là URL, điều này sẽ dẫn đến một ArgumentException.

Lần đầu tiên tôi thử Uri(Uri baseUri, string relativeUri)cách tiếp cận mới , thất bại đối với tôi vì các URI như http://www.mediawiki.org/wiki/Special:SpecialPages:

new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")

sẽ dẫn đến Special: SpecialPages, vì dấu hai chấm sau Specialđó biểu thị một lược đồ.

Vì vậy, cuối cùng tôi đã phải đi tuyến đường mdsharpe / Brian MacKays và phát triển nó thêm một chút để làm việc với nhiều phần URI:

public static string CombineUri(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Length > 0)
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }
    return uri;
}

Sử dụng: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")


1
+1: Bây giờ chúng ta đang nói ... Tôi sẽ thử điều này. Điều này thậm chí có thể trở thành câu trả lời mới được chấp nhận. Sau khi thử phương pháp Uri () mới, tôi thực sự không thích nó. Quá tinh tế.
Brian MacKay

Đây chính xác là những gì tôi cần! Không phải là một người hâm mộ của việc phải quan tâm nơi tôi đặt dấu gạch chéo, v.v ...
Gromer

+1 để cuộn trong kiểm tra null để nó không nổ tung.
NightOwl888

Đếm () phải là Độ dài để bạn không cần đưa Linq vào thư viện của mình.
PRman

Đây chính xác là những gì tôi đang tìm kiếm.
ThePeter

34

Dựa trên URL mẫu bạn cung cấp, tôi sẽ giả định rằng bạn muốn kết hợp các URL có liên quan đến trang web của bạn.

Dựa trên giả định này, tôi sẽ đề xuất giải pháp này là câu trả lời thích hợp nhất cho câu hỏi của bạn, đó là: "Path.Combine rất tiện dụng, có một chức năng tương tự trong khung cho URL không?"

Vì có một chức năng tương tự trong khung cho các URL mà tôi đề xuất chính xác là: phương pháp "VirtualPathUtility.Combine". Đây là liên kết tham chiếu MSDN: VirtualPathUtility.Combine Phương thức

Có một lưu ý: Tôi tin rằng điều này chỉ hoạt động đối với các URL liên quan đến trang web của bạn (nghĩa là bạn không thể sử dụng nó để tạo liên kết đến một trang web khác. Ví dụ var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");:).


+1 vì nó gần với những gì tôi đang tìm kiếm, mặc dù nó sẽ rất lý tưởng nếu nó hoạt động với bất kỳ url cũ nào. Tôi nhân đôi nó sẽ trở nên thanh lịch hơn nhiều so với những gì mdsharpe đề xuất.
Brian MacKay

2
Thông báo trước là chính xác, nó không thể hoạt động với uris tuyệt đối và kết quả luôn luôn tương đối từ gốc. Nhưng nó có một lợi ích bổ sung, nó xử lý dấu ngã, như với "~ /". Điều này làm cho nó một lối tắt cho Server.MapPathvà kết hợp.
Abel

25
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

12
path.Replace(Path.DirectorySeparatorChar, '/');
Jaider

5
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja - MSFT

1
Để có được nó, bạn phải xóa lần đầu tiên / trong giây thứ hai tức là "/ Hình ảnh" - / Path.Combine (" Http://MyUrl.com ", "Images / Image.jpg")
Mỗi

8
@SliverNinja Điều đó không đúng Giá trị của trường này là dấu gạch chéo ngược ('\') trên UNIX và dấu gạch chéo ('/') trên hệ điều hành Windows và Macintosh. Khi sử dụng Mono trên hệ thống Linux, bạn sẽ nhận được dấu phân cách sai.
dùng247702

6
Tất cả các bạn đang nhìn ra trên Trình phân tách thư mục đều quên rằng các chuỗi có thể đến từ một hệ điều hành khác với bạn hiện tại. Chỉ cần thay thế dấu gạch chéo ngược bằng dấu gạch chéo về phía trước và bạn được bảo vệ.
JeremyWeir

17

Tôi chỉ đưa ra một phương pháp mở rộng nhỏ:

public static string UriCombine (this string val, string append)
        {
            if (String.IsNullOrEmpty(val)) return append;
            if (String.IsNullOrEmpty(append)) return val;
            return val.TrimEnd('/') + "/" + append.TrimStart('/');
        }

Nó có thể được sử dụng như thế này:

"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");

12

Ví dụ dí dỏm, Ryan, để kết thúc với một liên kết đến chức năng. Làm tốt.

Một khuyến nghị Brian: nếu bạn bọc mã này trong một hàm, bạn có thể muốn sử dụng UriBuilder để bọc URL cơ sở trước lệnh gọi TryCreate.

Mặt khác, URL cơ sở PHẢI bao gồm lược đồ (trong đó UriBuilder sẽ giả sử http: //). Chỉ là một suy nghĩ:

public string CombineUrl(string baseUrl, string relativeUrl) {
    UriBuilder baseUri = new UriBuilder(baseUrl);
    Uri newUri;

    if (Uri.TryCreate(baseUri.Uri, relativeUrl, out newUri))
        return newUri.ToString();
    else
        throw new ArgumentException("Unable to combine specified url values");
}

10

Một cách dễ dàng để kết hợp chúng và đảm bảo nó luôn chính xác là:

string.Format("{0}/{1}", Url1.Trim('/'), Url2);

+1, mặc dù điều này rất giống với câu trả lời của mdsharpe, điều mà tôi đã cải thiện trong câu trả lời của mình. Phiên bản này hoạt động rất tốt trừ khi Url2 bắt đầu bằng / hoặc \ hoặc Url1 vô tình kết thúc bằng \ hoặc một trong hai trống! :)
Brian MacKay

9

Kết hợp nhiều phần của một URL có thể hơi khó khăn một chút. Bạn có thể sử dụng hàm tạo hai tham số Uri(baseUri, relativeUri)hoặc bạn có thể sử dụng Uri.TryCreate()hàm tiện ích.

Trong cả hai trường hợp, bạn có thể kết thúc trở về một kết quả không chính xác bởi vì các phương pháp này tiếp tục cắt bỏ những phần tương đối tắt của các tham số đầu tiên baseUri, tức là từ một cái gì đó như http://google.com/some/thingđể http://google.com.

Để có thể kết hợp nhiều phần vào một URL cuối cùng, bạn có thể sao chép hai chức năng bên dưới:

    public static string Combine(params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;

        var urlBuilder = new StringBuilder();
        foreach (var part in parts)
        {
            var tempUrl = tryCreateRelativeOrAbsolute(part);
            urlBuilder.Append(tempUrl);
        }
        return VirtualPathUtility.RemoveTrailingSlash(urlBuilder.ToString());
    }

    private static string tryCreateRelativeOrAbsolute(string s)
    {
        System.Uri uri;
        System.Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out uri);
        string tempUrl = VirtualPathUtility.AppendTrailingSlash(uri.ToString());
        return tempUrl;
    }

Mã đầy đủ với các bài kiểm tra đơn vị để chứng minh việc sử dụng có thể được tìm thấy tại https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs

Tôi có các bài kiểm tra đơn vị để bao gồm ba trường hợp phổ biến nhất:

Nhập mô tả hình ảnh ở đây


2
Nhìn có vẻ khá tốt đối với tôi. Mặc dù bạn có thể thay thế vòng lặp I bằng vòng lặp foreach để rõ ràng hơn.
Chris Marisic

Cảm ơn Chris. Tôi vừa thay đổi mã của mình để sử dụng Foreach.
Tin tưởng2014

1
+1 cho tất cả các nỗ lực thêm. Tôi cần duy trì câu hỏi này một chút cho một số câu trả lời được bình chọn cao hơn, bạn đã ném xuống cái găng tay. ;)
Brian MacKay

Xin lỗi, nhưng không đủ. Hoạt động trong một vài trường hợp bạn hiển thị nhưng không thể sử dụng được trong các kết hợp al. Ví dụ, dấu hai chấm trong đường dẫn sẽ gây hại.
Gábor

Bạn có thể vui lòng cho một ví dụ về những gì bạn có ý nghĩa? Tôi sẽ vui lòng khắc phục sự cố và giúp đỡ những người dùng tiếp theo.
Tin tưởng 2014

7

Tôi thấy UriBuilderlàm việc rất tốt cho việc này:

UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;

Xem Lớp UriBuilder - MSDN để biết thêm các hàm tạo và tài liệu.


4

Đây là phương pháp của Microsoft (OfficeDev PnP) UrlUtility.Combine :

    const char PATH_DELIMITER = '/';

    /// <summary>
    /// Combines a path and a relative path.
    /// </summary>
    /// <param name="path"></param>
    /// <param name="relative"></param>
    /// <returns></returns>
    public static string Combine(string path, string relative) 
    {
        if(relative == null)
            relative = String.Empty;

        if(path == null)
            path = String.Empty;

        if(relative.Length == 0 && path.Length == 0)
            return String.Empty;

        if(relative.Length == 0)
            return path;

        if(path.Length == 0)
            return relative;

        path = path.Replace('\\', PATH_DELIMITER);
        relative = relative.Replace('\\', PATH_DELIMITER);

        return path.TrimEnd(PATH_DELIMITER) + PATH_DELIMITER + relative.TrimStart(PATH_DELIMITER);
    }

Nguồn: GitHub


Dường như điều này có thể dành cho đường dẫn, thay vì URL.
Brian MacKay

@BrianMacKay Đồng ý rằng nó trông giống như vậy, nhưng nó thuộc lớp UrlUtility và được sử dụng trong bối cảnh kết hợp URL

2
Đã chỉnh sửa để làm rõ nó thuộc về lớp nào

Cẩn thận khi sử dụng Lớp này, phần còn lại của lớp chứa các tạo phẩm cụ thể của SharePoint.
Harry Berry

4

Tôi thấy hữu ích sau đây và có các tính năng sau:

  • Ném vào khoảng trống hoặc trắng
  • Có nhiều paramstham số cho nhiều phân đoạn Url
  • ném vào null hoặc trống

Lớp học

public static class UrlPath
{
   private static string InternalCombine(string source, string dest)
   {
      if (string.IsNullOrWhiteSpace(source))
         throw new ArgumentException("Cannot be null or white space", nameof(source));

      if (string.IsNullOrWhiteSpace(dest))
         throw new ArgumentException("Cannot be null or white space", nameof(dest));

      return $"{source.TrimEnd('/', '\\')}/{dest.TrimStart('/', '\\')}";
   }

   public static string Combine(string source, params string[] args) 
       => args.Aggregate(source, InternalCombine);
}

Xét nghiệm

UrlPath.Combine("test1", "test2");
UrlPath.Combine("test1//", "test2");
UrlPath.Combine("test1", "/test2");

// Result = test1/test2

UrlPath.Combine(@"test1\/\/\/", @"\/\/\\\\\//test2", @"\/\/\\\\\//test3\") ;

// Result = test1/test2/test3

UrlPath.Combine("/test1/", "/test2/", null);
UrlPath.Combine("", "/test2/");
UrlPath.Combine("/test1/", null);

// Throws an ArgumentException

@PeterMortensen cảm ơn vì đã chỉnh sửa
TheGeneral

Một số vấn đề với các bài kiểm tra: // result = test1 / test2 / test3 \ cho bài kiểm tra thứ 4 và bài kiểm tra ném cuối cùng mang lại cho ArgumentNullException thay vì ArgumentException
Moriya

3

Giải pháp chung của tôi:

public static string Combine(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Any())
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);

        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }

    return uri;
}

Phương pháp trợ giúp này rất linh hoạt và hoạt động tốt trong nhiều trường hợp sử dụng khác nhau. Cảm ơn bạn!
Shiva

3

Tôi đã tạo chức năng này sẽ giúp cuộc sống của bạn dễ dàng hơn:

    /// <summary>
    /// The ultimate Path combiner of all time
    /// </summary>
    /// <param name="IsURL">
    /// true - if the paths are Internet URLs, false - if the paths are local URLs, this is very important as this will be used to decide which separator will be used.
    /// </param>
    /// <param name="IsRelative">Just adds the separator at the beginning</param>
    /// <param name="IsFixInternal">Fix the paths from within (by removing duplicate separators and correcting the separators)</param>
    /// <param name="parts">The paths to combine</param>
    /// <returns>the combined path</returns>
    public static string PathCombine(bool IsURL , bool IsRelative , bool IsFixInternal , params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;
        char separator = IsURL ? '/' : '\\';

        if (parts.Length == 1 && IsFixInternal)
        {
            string validsingle;
            if (IsURL)
            {
                validsingle = parts[0].Replace('\\' , '/');
            }
            else
            {
                validsingle = parts[0].Replace('/' , '\\');
            }
            validsingle = validsingle.Trim(separator);
            return (IsRelative ? separator.ToString() : string.Empty) + validsingle;
        }

        string final = parts
            .Aggregate
            (
            (string first , string second) =>
            {
                string validfirst;
                string validsecond;
                if (IsURL)
                {
                    validfirst = first.Replace('\\' , '/');
                    validsecond = second.Replace('\\' , '/');
                }
                else
                {
                    validfirst = first.Replace('/' , '\\');
                    validsecond = second.Replace('/' , '\\');
                }
                var prefix = string.Empty;
                if (IsFixInternal)
                {
                    if (IsURL)
                    {
                        if (validfirst.Contains("://"))
                        {
                            var tofix = validfirst.Substring(validfirst.IndexOf("://") + 3);
                            prefix = validfirst.Replace(tofix , string.Empty).TrimStart(separator);

                            var tofixlist = tofix.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                            validfirst = separator + string.Join(separator.ToString() , tofixlist);
                        }
                        else
                        {
                            var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                            validfirst = string.Join(separator.ToString() , firstlist);
                        }

                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                    else
                    {
                        var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                        validfirst = string.Join(separator.ToString() , firstlist);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                }
                return prefix + validfirst.Trim(separator) + separator + validsecond.Trim(separator);
            }
            );
        return (IsRelative ? separator.ToString() : string.Empty) + final;
    }

Nó hoạt động cho các URL cũng như các đường dẫn bình thường.

Sử dụng:

    // Fixes internal paths
    Console.WriteLine(PathCombine(true , true , true , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: /folder 1/folder2/folder3/somefile.ext

    // Doesn't fix internal paths
    Console.WriteLine(PathCombine(true , true , false , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : /folder 1//////////folder2////folder3/somefile.ext

    // Don't worry about URL prefixes when fixing internal paths
    Console.WriteLine(PathCombine(true , false , true , @"/\/\/https:/\/\/\lul.com\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: https://lul.com/folder2/folder3/somefile.ext

    Console.WriteLine(PathCombine(false , true , true , @"../../../\\..\...\./../somepath" , @"anotherpath"));
    // Result: \..\..\..\..\...\.\..\somepath\anotherpath

3

Tại sao không chỉ sử dụng như sau.

System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")

Tôi đã tìm kiếm phiên bản PowerShell của phiên bản này: [System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")tuy nhiên điều này không thành công với kết quả là : /Images/Image.jpg. Xóa /khỏi subPath thứ hai và nó hoạt động:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Bỏ qua

Ý tưởng hay, nhưng nó thất bại, khi một trong các tham số là null.
pholpar

2

Quy tắc trong khi kết hợp URL với URI

Để tránh hành vi lạ có một quy tắc phải tuân theo:

  • Đường dẫn (thư mục) phải kết thúc bằng '/'. Nếu đường dẫn kết thúc mà không có '/', phần cuối cùng được coi như tên tệp và nó sẽ được nối với nhau khi cố gắng kết hợp với phần URL tiếp theo.
  • Có một ngoại lệ: địa chỉ URL cơ sở (không có thông tin thư mục) không cần kết thúc bằng '/'
  • phần đường dẫn không được bắt đầu bằng '/'. Nếu nó bắt đầu bằng '/', mọi thông tin tương đối hiện có từ URL sẽ bị hủy ... thêm một string.Emptyđường dẫn một phần cũng sẽ xóa thư mục tương đối khỏi URL!

Nếu bạn làm theo các quy tắc ở trên, bạn có thể kết hợp URL với mã bên dưới. Tùy thuộc vào tình huống của bạn, bạn có thể thêm nhiều phần 'thư mục' vào URL ...

        var pathParts = new string[] { destinationBaseUrl, destinationFolderUrl, fileName };

        var destination = pathParts.Aggregate((left, right) =>
        {
            if (string.IsNullOrWhiteSpace(right))
                return left;

            return new Uri(new Uri(left), right).ToString();
        });

2

Nếu bạn không muốn thêm phụ thuộc của bên thứ ba như Flurl hoặc tạo phương thức tiện ích mở rộng tùy chỉnh, trong ASP.NET Core (cũng có sẵn trong Microsoft.Owin), bạn có thể sử dụng PathStringnhằm mục đích xây dựng URI đường dẫn. Sau đó, bạn có thể tạo URI đầy đủ của mình bằng cách sử dụng kết hợp này UriUriBuilder .

Trong trường hợp này, nó sẽ là:

new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())

Điều này cung cấp cho bạn tất cả các bộ phận cấu thành mà không phải chỉ định các dấu phân cách trong URL cơ sở. Thật không may, PathStringyêu cầu đó /được thêm vào từng chuỗi nếu không nó thực sự ném một ArgumentException! Nhưng ít nhất bạn có thể xây dựng URI của mình một cách xác định theo cách dễ kiểm tra đơn vị.


2

Vì vậy, tôi có một cách tiếp cận khác, tương tự như mọi người đã sử dụng UriBuilder.

Tôi không muốn tách BaseUrl của mình (có thể chứa một phần của đường dẫn - ví dụ: http://mybaseurl.com/dev/ ) như javajavajavajavajava đã làm.

Đoạn mã sau hiển thị mã + Kiểm tra.

Cẩn thận: Giải pháp này hạ thấp máy chủ và nối thêm một cổng. Nếu điều này là không mong muốn, người ta có thể viết một chuỗi đại diện bằng cách tận dụng UriThuộc tính của UriBuilder.

  public class Tests
  {
         public static string CombineUrl (string baseUrl, string path)
         {
           var uriBuilder = new UriBuilder (baseUrl);
           uriBuilder.Path = Path.Combine (uriBuilder.Path, path);
           return uriBuilder.ToString();
         }

         [TestCase("http://MyUrl.com/", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath/", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         public void Test1 (string baseUrl, string path, string expected)
         {
           var result = CombineUrl (baseUrl, path);

           Assert.That (result, Is.EqualTo (expected));
         }
  }

Đã thử nghiệm với .NET Core 2.1 trên Windows 10.

Tại sao điều này làm việc?

Mặc dù Path.Combinesẽ trả về Backslashes (trên Windows ít nhất), UriBuilder xử lý trường hợp này trong Setter of Path.

Lấy từ https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs (chú ý cuộc gọi đến string.Replace)

[AllowNull]
public string Path
{
      get
      {
          return _path;
      }
      set
      {
          if ((value == null) || (value.Length == 0))
          {
              value = "/";
          }
          _path = Uri.InternalEscapeString(value.Replace('\\', '/'));
          _changed = true;
      }
 }

Đây có phải là cách tiếp cận tốt nhất?

Chắc chắn giải pháp này là khá tự mô tả (ít nhất là theo ý kiến ​​của tôi). Nhưng bạn đang dựa vào tài liệu không có giấy tờ (ít nhất là tôi không tìm thấy gì với tìm kiếm nhanh trên google) "tính năng" từ API .NET. Điều này có thể thay đổi với bản phát hành trong tương lai, vì vậy vui lòng bao gồm Phương pháp bằng các Bài kiểm tra.

Có các bài kiểm tra trong https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FeftalTests/UriBuilderTests.cs ( Path_Get_Set) để kiểm tra, nếu \nó được chuyển đổi chính xác.

Lưu ý bên lề: Người ta cũng có thể làm việc UriBuilder.Uritrực tiếp với tài sản, nếu uri sẽ được sử dụng cho một System.Urictor.


Đây là một cách tiếp cận rất đáng tin cậy. Thumbs up cho bài kiểm tra đơn vị !!
agssol

2

Đối với bất kỳ ai đang tìm kiếm một lớp lót và chỉ muốn tham gia các phần của đường dẫn mà không tạo phương thức mới hoặc tham chiếu thư viện mới hoặc xây dựng giá trị URI và chuyển đổi nó thành chuỗi, sau đó ...

string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");

Nó khá cơ bản, nhưng tôi không thấy bạn cần gì hơn nữa. Nếu bạn sợ nhân đôi '/' thì bạn có thể chỉ cần làm một.Replace("//", "/") sau đó. Nếu bạn sợ thay thế nhân đôi '//' trong 'https: //', thì thay vào đó hãy tham gia, thay thế nhân đôi '/', sau đó tham gia url trang web (tuy nhiên tôi khá chắc chắn rằng hầu hết các trình duyệt sẽ tự động chuyển đổi bất cứ thứ gì có 'https:' ở phía trước để đọc theo đúng định dạng). Điều này sẽ trông giống như:

string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));

Có rất nhiều câu trả lời ở đây sẽ xử lý tất cả các câu hỏi trên, nhưng trong trường hợp của tôi, tôi chỉ cần nó một lần ở một địa điểm và sẽ không cần phải phụ thuộc nhiều vào nó. Ngoài ra, thật dễ dàng để xem những gì đang xảy ra ở đây.

Xem: https://docs.microsoft.com/en-us/dotnet/api/system.opes.join?view=netframework-4.8


1

Sử dụng:

    private Uri UriCombine(string path1, string path2, string path3 = "", string path4 = "")
    {
        string path = System.IO.Path.Combine(path1, path2.TrimStart('\\', '/'), path3.TrimStart('\\', '/'), path4.TrimStart('\\', '/'));
        string url = path.Replace('\\','/');
        return new Uri(url);
    }

Nó có lợi ích của việc cư xử chính xác như thế nào Path.Combine.


1

Đây là cách tiếp cận của tôi và tôi cũng sẽ sử dụng nó cho bản thân mình:

public static string UrlCombine(string part1, string part2)
{
    string newPart1 = string.Empty;
    string newPart2 = string.Empty;
    string seperator = "/";

    // If either part1 or part 2 is empty,
    // we don't need to combine with seperator
    if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
    {
        seperator = string.Empty;
    }

    // If part1 is not empty,
    // remove '/' at last
    if (!string.IsNullOrEmpty(part1))
    {
        newPart1 = part1.TrimEnd('/');
    }

    // If part2 is not empty,
    // remove '/' at first
    if (!string.IsNullOrEmpty(part2))
    {
        newPart2 = part2.TrimStart('/');
    }

    // Now finally combine
    return string.Format("{0}{1}{2}", newPart1, seperator, newPart2);
}

Điều này chỉ được chấp nhận cho trường hợp của bạn. Có những trường hợp có thể phá vỡ mã của bạn. Ngoài ra, bạn đã không thực hiện mã hóa đúng các phần của đường dẫn. Đây có thể là một lỗ hổng lớn khi nói đến cuộc tấn công kịch bản chéo trang.
Tin tưởng 2014

Tôi đồng ý với quan điểm của bạn. Mã được cho là chỉ cần kết hợp đơn giản hai phần url.
Amit Bhagat

1

Dùng cái này:

public static class WebPath
{
    public static string Combine(params string[] args)
    {
        var prefixAdjusted = args.Select(x => x.StartsWith("/") && !x.StartsWith("http") ? x.Substring(1) : x);
        return string.Join("/", prefixAdjusted);
    }
}

Liên lạc tốt đẹp với 'WebPath'. :) Mặc dù vậy, mã có thể dày đặc một cách không cần thiết - thật khó để tôi liếc nhìn cái này và nói, vâng, nó thật hoàn hảo. Nó làm cho tôi muốn xem các bài kiểm tra đơn vị. Có lẽ đó chỉ là tôi!
Brian MacKay

1
x.StartsWith ("/") &&! x.StartsWith ("http") - tại sao kiểm tra http? bạn đạt được gì
chim cánh cụt

Bạn không muốn cố gắng loại bỏ dấu gạch chéo nếu nó bắt đầu bằng http.
Martin Murphy

@BrianMacKay, tôi không chắc chắn hai lớp bảo đảm kiểm tra đơn vị nhưng nếu bạn muốn thoải mái cung cấp một bài kiểm tra. Nó không giống như tôi chấp nhận các bản vá hoặc bất cứ điều gì, nhưng hãy thoải mái chỉnh sửa đề xuất.
Martin Murphy

1

Tôi thấy rằng hàm Uritạo lật '\' thành '/'. Vì vậy, bạn cũng có thể sử dụng Path.Combine, với các nhà Urixây dựng.

 Uri baseUri = new Uri("http://MyUrl.com");
 string path = Path.Combine("Images", "Image.jpg");
 Uri myUri = new Uri(baseUri, path);

1

Đối với những gì nó có giá trị, ở đây một vài phương pháp mở rộng. Cái đầu tiên sẽ kết hợp các đường dẫn và cái thứ hai thêm các tham số vào URL.

    public static string CombineUrl(this string root, string path, params string[] paths)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return root;
        }

        Uri baseUri = new Uri(root);
        Uri combinedPaths = new Uri(baseUri, path);

        foreach (string extendedPath in paths)
        {
           combinedPaths = new Uri(combinedPaths, extendedPath);
        }

        return combinedPaths.AbsoluteUri;
    }

    public static string AddUrlParams(this string url, Dictionary<string, string> parameters)
    {
        if (parameters == null || !parameters.Keys.Any())
        {
            return url;
        }

        var tempUrl = new StringBuilder($"{url}?");
        int count = 0;

        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            if (count > 0)
            {
                tempUrl.Append("&");
            }

            tempUrl.Append($"{WebUtility.UrlEncode(parameter.Key)}={WebUtility.UrlEncode(parameter.Value)}");
            count++;
        }

        return tempUrl.ToString();
    }

1

Như tìm thấy trong các câu trả lời khác, hoặc mới Uri()hoặc TryCreate()có thể đánh dấu. Tuy nhiên, Uri cơ sở phải kết thúc /và người thân phải KHÔNG bắt đầu bằng /; nếu không, nó sẽ loại bỏ phần đuôi của Url cơ sở

Tôi nghĩ rằng điều này được thực hiện tốt nhất như là một phương pháp mở rộng, tức là

public static Uri Append(this Uri uri, string relativePath)
{
    var baseUri = uri.AbsoluteUri.EndsWith('/') ? uri : new Uri(uri.AbsoluteUri + '/');
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri, relative);
}

và sử dụng nó:

var baseUri = new Uri("http://test.com/test/");
var combinedUri =  baseUri.Append("/Do/Something");

Về hiệu năng, điều này tiêu tốn nhiều tài nguyên hơn mức cần thiết, bởi vì lớp Uri thực hiện nhiều phân tích cú pháp và xác nhận; một hồ sơ rất thô sơ (Debug) đã thực hiện một triệu thao tác trong khoảng 2 giây. Điều này sẽ hoạt động trong hầu hết các kịch bản, tuy nhiên để hiệu quả hơn, tốt hơn là thao tác mọi thứ dưới dạng chuỗi, việc này mất 125 mili giây cho 1 triệu thao tác. I E

public static string Append(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return baseUri + relative;
}

Và nếu bạn vẫn muốn trả về một URI, phải mất khoảng 600 mili giây cho 1 triệu thao tác.

public static Uri AppendUri(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri + relative);
}

Tôi hi vọng cái này giúp được.


1

Tôi nghĩ rằng điều này sẽ giúp bạn linh hoạt hơn khi bạn có thể xử lý nhiều phân đoạn đường dẫn như bạn muốn:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.