URLのPath.Combine?


1244

Path.Combineは便利ですが、URLの .NETフレームワークにも同様の機能はありますか?

私はこのような構文を探しています:

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

返すでしょう:

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


14
Flurlには、Url.Combineそれを行うメソッドが含まれています。
Todd Menier、2014

2
実際には、//はブラウザではなく、Webサイトまたはサーバーのルーティングによって処理されます。アドレスバーに入力したものを送信します。そのため、http://の代わりにhtp://と入力すると問題が発生します。そのため、//は一部のサイトで重大な問題を引き起こす可能性があります。URLに//がある場合に404​​をスローする特定のWebサイトを処理するクローラーの.dllを書いています。
デイブゴードン

回答:


73

そこトッド・ムニエのコメントは上記であることをFlurlが含まれますUrl.Combine

詳細:

Url.Combineは基本的にURLのPath.Combineであり、パーツ間に1つだけの区切り文字を保証します。

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" 

NuGetでFlurl.Httpを取得します

PM>インストールパッケージFlurl.Http

または、HTTP機能なしのスタンドアロンURLビルダー取得します。

PM>インストールパッケージのFlurl


4
さて、この質問は多くのトラフィックを取得し、1000以上の賛成票での回答は実際にはすべての場合に機能するわけではありません。数年後、私は実際にこれにFlurlを使用するので、これを受け入れます。それは私が遭遇したすべてのケースで機能するようです。依存関係を取りたくない人のために、私もうまくいく答えを投稿しました。
ブライアンマッケイ2018年

使用Flurlしないで軽量バージョンを使用する場合は、github.com / jean
lourenco /

1157

Uri これを行うコンストラクタがあります: new Uri(Uri baseUri, string relativeUri)

次に例を示します。

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

エディターからの注意:このメソッドは期待どおりに機能しないことに注意してください。場合によってはbaseUriの一部を切り取ることができます。コメントやその他の回答をご覧ください。


369
私はUriクラスの使用が好きですが、残念ながら、OPが要求したとおり、Path.Combineのように動作しません。たとえば、new Uri(new Uri( " test.com/mydirectory/")、 "/helloworld.aspx")。ToString()は " test.com/helloworld.aspx " を提供します。Path.Combineスタイルの結果が必要な場合、これは正しくありません。
ドクタージョーンズ

195
それはすべてスラッシュです。相対パス部分がスラッシュで始まる場合は、説明したとおりに動作します。ただし、スラッシュを省略すると、期待どおりに機能します(2番目のパラメーターにスラッシュがないことに注意してください):new Uri(new Uri( " test.com/mydirectory/")、 "helloworld.aspx" ).ToString()の結果は「test.com/mydirectory/helloworld.aspx」になります。Path.Combineも同様に動作します。相対パスパラメータがスラッシュで始まる場合、相対パスのみが返され、それらは結合されません。
Joel Beckham、

70
baseUriが "test.com/mydirectory/mysubdirectory"の場合、結果は "test.com/mydirectory/mysubdirectory/helloworld.aspx"ではなく "test.com/mydirectory/helloworld.aspx"になります。微妙な違いは、最初のパラメーターの末尾にスラッシュがないことです。私はすべて既存のフレームワークメソッドを使用することに賛成です。もし私がすでに末尾のスラッシュを持っている必要があるなら、partUrl1 + partUrl2を実行することで匂いがずっと少なくなると思います。文字列連結を行わないため。
カール・

64
URI結合メソッドが必要な唯一の理由は、末尾のスラッシュを確認する必要がないためです。アプリケーションがルートにある場合、Request.ApplicationPathは「/」ですが、ルートでない場合は「/ foo」です。
11年

24
これは問題に答えないので、私はこの答えを-1にします。Path.Combineを使用するときのように、URLを結合するときは、末尾の/を気にする必要はありません。これで、あなたは気にする必要があります。私は上記のブライアンマッケイまたはmdsharpeの解を好む
Baptiste Pernet

161

これは適切な単純な解決策かもしれません:

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

7
+1:これは相対スタイルのパス(../../whatever.html)を処理しませんが、私はその単純さのためにこのパスが好きです。'\'文字のトリムも追加します。
ブライアンマッケイ

3
この完全に具体化されたバージョンについては、私の回答を参照してください。
ブライアンマッケイ

149

あなたが使用する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);
}

戻ります:

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


53
+1:これは問題ありませんが、出力パラメーターに不合理な問題があります。;)
ブライアンマッケイ、

10
@ブライアン:役立つ場合は、すべてのTryXXXメソッド(int.TryParseDateTime.TryParseExact)にこの出力パラメーターがあり、ifステートメントで簡単に使用できます。ところで、この例のRyanのように変数を初期化する必要はありません。
アベル

41
この回答は、Joelと同じ問題を抱えています。Jointest.com/mydirectory/と参加すると、/helloworld.aspx結果的に、test.com/helloworld.aspx希望どおりではないように見えます。
Matt Kocaj 2013

3
こんにちは、これは次の理由で失敗しました:if(Uri.TryCreate(new Uri( " localhost / MyService /")、 "/ Event / SomeMethod?abc = 123"、out result)){Console.WriteLine(result); 結果は次のように表示されます:localhost / Event / SomeMethod?abc = 123 注:「http://」は、ここでベースUriからstackoverflowに置き換えられます
Faisal Mq

3
@FaisalMqルート相対の2番目のパラメーターを渡したため、これは正しい動作です。2番目のパラメーターの先頭の/を省略した場合は、期待どおりの結果が得られます。
トム・リント

127

ここにはすでにいくつかの素晴らしい答えがあります。mdsharpeの提案に基づいて、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('/'))));
    }
}

そして使用例:

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

これにより、http://example.com/subpath/part1/part2が生成されます


2
このソリューションでは、Path.Combine()とよく似たUriUtils.Combine( "base url"、 "part1"、 "part2"、...)静的メソッドを書くのは簡単です。いいね!
angularsen '19年

相対URIをサポートするには、UriコンストラクターでAbsoluteUriおよびUriKind.AbsoluteOrRelativeの代わりにToString()を使用する必要がありました。
angularsen

相対的なUrisについてのヒントをありがとう。残念ながら、Uriは常に関連パスの処理を簡単にしません。これは、常にRequest.ApplicationPathに関連する問題がいくつかあるためです。おそらく、新しいUri(HttpContext.Current.Request.ApplicationPath)をベースとして使用して、Appendを呼び出すこともできますか?これは絶対パスを提供しますが、サイト構造内のどこでも機能するはずです。
Ales Potocnik Hahonina

すごい。それが他の誰かを助けてうれしい。これをしばらく使用していて、問題はありませんでした。
Ales Potocnik Hahonina 2013

追加するパスのいずれかがnullでも空の文字列でもないかどうかのチェックも追加しました。
n.podbielski 2016年

92

Ryan Cookの答えは私が求めているものに近く、他の開発者にはより適切かもしれません。ただし、文字列の先頭にhttp://が追加され、一般に、後の書式よりも少し多くの書式設定が行われます。

また、私のユースケースでは、相対パスの解決は重要ではありません。

mdsharpの回答には、優れたアイデアの種も含まれていますが、実際の実装では、完成するためにさらにいくつかの詳細が必要でした。これはそれを具体化する試みです(そして私はこれを本番で使用しています):

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

このコードは、たまたま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
詳細についてArgumentNullException("url1")言えば、もし議論がそうであるならば、Nothingどうですか?すみません、うるさいだけです;-)。バックスラッシュはURIでは何もしないことに注意してください(バックスラッシュがある場合は、削除しないでください)。TrimXXXから削除できます。
エイベル

4
params string []を使用して再帰的に結合し、3つ以上の組み合わせを許可する
Jaider

4
これがPath.Combineのような基本クラスライブラリにあればいいのにと思います。
Uriah Blatherwick 2014

1
@MarkHurdコードを再度編集したので、動作はC#と同じで、構文的にも同じです。
JJS 2016

1
@BrianMacKay私はそれを壊し、markhurdが私の間違いを指摘してロールバックしました、私は再び更新しました...乾杯
JJS

36

「|」などの文字が含まれている可能性があるため、Path.Combineが機能しません。QueryString引数、したがってURLでは、ArgumentExceptionが発生します。

私は最初に新しいUri(Uri baseUri, string relativeUri)アプローチを試しましたが、次のようなURIのために失敗しましたhttp://www.mediawiki.org/wiki/Special:SpecialPages

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

その後のコロンSpecialがスキームを示すため、Special:SpecialPagesになります。

だから私はようやくmdsharpe / Brian MacKaysのルートをたどり、複数の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;
}

使用法: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")


1
+1:これから話しましょう...これを試してみるつもりです。これは、最終的には新しい受け入れられた答えになるかもしれません。新しいUri()メソッドを試した後、私は本当にそれが好きではありません。フィニッキーすぎる。
ブライアンマッケイ2011

これはまさに私が必要としたものです!末尾のスラッシュなどをどこに置くかを気にする必要がなかった...
Gromer

+1は、nullチェックをロールインして、爆発しないようにします。
NightOwl888 14

そのためだけにライブラリにLinqを含める必要がないように、Count()はLengthにする必要があります。
PRMan

これはまさに私が探していたものでした。
ThePeter

34

提供されたサンプルURLに基づいて、サイトに関連するURLを結合することを想定します。

この仮定に基づいて、「Path.Combineは便利ですが、URLのフレームワークに同様の機能はありますか?」という質問に対する最も適切な回答として、このソリューションを提案します。

URLのフレームワークにも同様の関数があるので、正しい方法は「VirtualPathUtility.Combine」メソッドです。これは、MSDNリファレンスリンクです。VirtualPathUtility.Combineメソッド

注意点が1つあります。これは、自分のサイトに関連するURLに対してのみ機能すると思います(つまり、これを使用して別のWebサイトへのリンクを生成することはできません。たとえば、var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");)。


+1は、私が探しているものに近いためです。ただし、古いURLで機能する場合は理想的です。mdsharpeが提案したものよりもはるかにエレガントになります。
ブライアンマッケイ

2
警告は正しいです。絶対URIでは機能せず、結果は常にルートからの相対です。しかし、これには追加の利点があり、「〜/」と同様にチルダを処理します。これは、それServer.MapPathを結合するためのショートカットになります。
エイベル

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 2012年

1
それをwrkに取得するには、uを最初に削除する必要があります/ "/ Images"-/ Path.Combine( " Http://MyUrl.com "、 "Images / Image.jpg")
Per G

8
@SliverNinja不正解です。このフィールドの値は、UNIXではバックスラッシュ( '\')、WindowsおよびMacintoshオペレーティングシステムではスラッシュ( '/')です。LinuxシステムでMonoを使用すると、間違ったセパレーターが表示されます。
user247702 14年

6
Directory Separatorに熱狂しているすべてのyallは、文字列が現在使用しているものとは異なるOSからのものである可能性があることを忘れています。バックスラッシュをスラッシュに置き換えるだけでカバーされます。
JeremyWeir

17

私は小さな拡張メソッドをまとめました:

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('/');
        }

次のように使用できます。

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

12

機知に富んだ例、ライアン、関数へのリンクで終わる。よくやった。

1つの推奨事項Brian:このコードを関数でラップする場合、UriBuilderを使用して、TryCreate呼び出しの前にベースURLをラップすることができます。

それ以外の場合は、ベースURLにスキームを含める必要があります(UriBuilderはhttp://を想定します)。ちょっとした考え:

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

それらを組み合わせて常に正しいことを確認する簡単な方法は次のとおりです。

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

+1。これはmdsharpeの回答と非常によく似ていますが、私の回答で改善しました。このバージョンは、Url2が/または\で始まるか、Url1が誤って\で終わるか、どちらかが空でない限り、うまく機能します。:)
ブライアンマッケイ

9

URLの複数の部分を組み合わせるのは少し難しいかもしれません。2パラメータコンストラクタUri(baseUri, relativeUri)を使用するか、Uri.TryCreate()ユーティリティ関数を使用できます。

どちらの場合でも、これらのメソッドは最初のパラメーターの相対部分baseUri、つまりhttp://google.com/some/thingto からの相対部分を切り捨て続けるため、誤った結果を返す可能性がありますhttp://google.com

複数の部分を最終的なURLに結合できるようにするには、以下の2つの関数をコピーできます。

    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;
    }

使用方法を示す単体テストを含む完全なコードは、https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.csにあります。

最も一般的な3つのケースをカバーする単体テストがあります。

ここに画像の説明を入力してください


2
私にはかなりよさそうだ。より明確にするために、Iループをforeachループに置き換えることもできます。
Chris Marisic 2014年

クリス、ありがとう。Foreachを使用するようにコードを変更しました。
Believe2014

1
すべての余分な努力のために+1。投票数の多い回答のいくつかについては、この質問を少し維持する必要があります。ガントレットを倒しました。;)
ブライアンマッケイ2014年

申し訳ありませんが、十分ではありません。あなたが示すいくつかのケースで機能しますが、すべての組み合わせで使用できるわけではありません。たとえば、パス内のコロンは害を引き起こします。
ガボル

意味の例を挙げていただけますか?私は問題を修正し、次のユーザーを支援させていただきます。
Believe2014

7

UriBuilderはこの種のもののために本当にうまくいったことがわかりました:

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

その他のコンストラクターとドキュメントについては、UriBuilderクラス-MSDNを参照してください。


4

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);
    }

出典:GitHub


これはURLではなくパスの可能性があるようです。
Brian MacKay、2015年

@BrianMacKayそれはそのように見えることに同意しましたが、それはUrlUtilityクラスからのものであり、URLを組み合わせるコンテキストで使用されています

2
所属するクラスを明確にするために編集

このクラスを使用するときは注意してください。クラスの残りの部分にはSharePoint固有のアーティファクトが含まれています。
Harry Berry

4

私は次のものが役に立ち、次の機能を備えています:

  • nullまたは空白でスロー
  • params複数のURLセグメントの複数のパラメーターを受け取ります
  • nullまたは空をスローします

クラス

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);
}

テスト

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編集への感謝
TheGeneral

テストに関するいくつかの問題:// 4番目のテストのResult = test1 / test2 / test3 \および最後のthrowsテストでは、ArgumentExceptionではなくArgumentNullExceptionが発生する
Moriya

3

私の一般的な解決策:

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;
}

このヘルパーメソッドは非常に柔軟性があり、さまざまな使用例でうまく機能します。ありがとうございました!
Shiva

3

私はあなたの人生を楽にするこの関数を作りました:

    /// <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;
    }

これは、URLと通常のパスで機​​能します。

使用法:

    // 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

以下を使用しないでください。

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

私はこれのPowerShellバージョンを探していましたが、これは次[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")の結果で失敗します:/Images/Image.jpg。削除/二サブパスから、それが動作します:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Underverse

いい考えですが、パラメータの1つがnullの場合は失敗します。
ホルパー

2

URLとURIを組み合わせる際のルール

奇妙な振る舞いを避けるために、従うべきルールが1つあります。

  • パス(ディレクトリ)は「/」で終わる必要があります。パスが「/」なしで終了する場合、最後の部分はファイル名のように扱われ、次のURL部分と結合しようとすると連結されます。
  • 例外が1つあります。ベースURLアドレス(ディレクトリ情報なし)の末尾が「/」である必要はありません。
  • パス部分は「/」で始まってはなりません。「/」で始まる場合、URLからの既存の相対情報はすべて削除されます。string.Emptyパーツパスを追加すると、URLからも相対ディレクトリが削除されます。

上記のルールに従う場合、URLを以下のコードと組み合わせることができます。状況に応じて、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

Flurlなどのサードパーティの依存関係を追加したくない場合や、ASP.NET Core(Microsoft.Owinでも利用可能)でカスタム拡張メソッドを作成したくない場合PathStringは、URIの構築を目的としたものを使用できます。パス。次に、これUriとの組み合わせを使用して完全なURIを作成できますUriBuilder

この場合は、次のようになります。

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

これにより、ベースURLで区切り文字を指定する必要なく、すべての構成部分が得られます。残念ながら、各文字列の前にが付加されているPathString必要が/あります。そうでない場合、実際にはArgumentException!しかし、少なくとも、簡単に単体テストが可能な方法でURIを確定的に構築できます。


2

そのため、UriBuilderを使用したすべての人に似た別のアプローチがあります。

-私は(例えば、パスの一部含めることができます私のBASEURL分割したくなかったhttp://mybaseurl.com/dev/として)javajavajavajavajavaはなかったが。

次のスニペットは、コードとテストを示しています。

注意:このソリューションは、ホストを小文字にし、ポートを追加します。これが望ましくない場合は、たとえばのUriプロパティを利用して文字列表現を記述できます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));
         }
  }

Windows 10の.NET Core 2.1でテスト済み。

なぜこれが機能するのですか?

Path.Combine(Windowsでは少なくとも)バックスラッシュを返しますが、UriBuilderはSetter ofでこのケースを処理しPathます。

https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.csから取得(への呼び出しに注意してくださいstring.Replace

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

これは最善の方法ですか?

確かに、このソリューションはかなり自己記述的です(少なくとも私の意見では)。しかし、あなたは.NET APIのドキュメント化されていない(少なくとも、Googleのクイック検索で何も見つからなかった)「機能」に依存しています。これは将来のリリースで変更される可能性があるため、テストによる方法をカバーしてください。

https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.csPath_Get_Set)に\は、が正しく変換されているかどうかをチェックするテストがあります。

補足:UriBuilder.Uri uriがSystem.Urictorに使用される場合は、プロパティを直接操作することもできます。


これは非常に信頼できるアプローチです。ユニットテストに賛成!
aggsol

2

ワンライナーを探していて、新しいメソッドを作成したり、新しいライブラリを参照したり、URI値を構築したり、それを文字列に変換したりせずに、パスの一部を結合したい場合は、...

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

かなり基本的なことですが、それ以上必要なものはわかりません。「/」が2倍になるのが怖い場合は、.Replace("//", "/")あとで簡単に行うことができます。「https://」の二重の「//」を置き換えるのが怖い場合は、代わりに1つの結合を行い、二重の「/」を置き換えてから、WebサイトのURLに結合します(ただし、ほとんどのブラウザーは自動的に正しい形式で読み取るために、先頭に「https:」があるものをすべて変換します)。これは次のようになります。

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

上記のすべてを処理する答えはたくさんありますが、私の場合は、1つの場所で1回だけ必要であり、大きく依存する必要はありません。また、ここで何が行われているのかを簡単に確認できます。

参照:https : //docs.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8


1

使用する:

    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);
    }

とまったく同じように動作するという利点がありPath.Combineます。


1

これが私のアプローチであり、私自身もそれを使用します。

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);
}

これはあなたの場合にのみ許容されます。コードを壊す可能性のあるケースがあります。また、パスの一部を適切にエンコードしていません。クロスサイトスクリプティング攻撃に関しては、これは大きな脆弱性になる可能性があります。
Believe2014

私はあなたの主張に同意します。このコードは、2つのURL部分を単純に組み合わせるだけのものです。
アミットバガット2014年

1

これを使って:

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);
    }
}

「WebPath」を使った素敵なタッチ。:)コードは不必要に密であるかもしれません-私がこれをちらっと見て、はい、それは完璧だと言うのは難しいです。単体テストを見たいです。多分それは私だけです!
Brian MacKay

1
x.StartsWith( "/")&&!x.StartsWith( "http")-なぜhttpチェックですか?あなたは何を得るのですか?
ペンガート

スラッシュがhttpで始まる場合は、スラッシュを取り除こうとする必要はありません。
マーティンマーフィー

@BrianMacKay、私は2つのライナーがユニットテストを保証するかどうかわかりませんが、必要に応じてユニットテストを提供してください。パッチなどを受け入れるのではなく、提案を自由に編集してください。
マーティンマーフィー

1

Uriコンストラクターが '\'を '/'に反転することがわかりました。したがってPath.CombineUriコンストラクタでを使用することもできます。

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

1

価値のあるものとして、ここでいくつかの拡張メソッドを紹介します。1つ目はパスを組み合わせ、2つ目はパラメータを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

他の答えで見られるように、新しいUri()TryCreate()、ダニを行うことができます。ただし、ベースUriで終わる必要が/あり、親戚はで始まってはなりません/。それ以外の場合は、ベースURLの末尾部分を削除します

これは拡張メソッドとして行うのが最善だと思います。

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);
}

そしてそれを使うには:

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

パフォーマンスの点では、これは必要以上のリソースを消費します。これは、Uriクラスが多くの解析と検証を行うためです。非常に大まかなプロファイリング(デバッグ)は、約2秒で100万回の操作を実行しました。これはほとんどのシナリオで機能しますが、より効率的にするには、すべてを文字列として操作する方が良いです。これは、100万回の操作に125ミリ秒かかります。すなわち

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;
}

それでもURIを返したい場合は、100万回の操作に約600ミリ秒かかります。

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);
}

これがお役に立てば幸いです。


1

必要な数のパスセグメントを処理できるため、これにより柔軟性が高まると思います。

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.