58 Stimmen

guid zu base64, für URL

Frage: Gibt es eine bessere Möglichkeit, dies zu tun?

VB.Net

Function GuidToBase64(ByVal guid As Guid) As String
    Return Convert.ToBase64String(guid.ToByteArray).Replace("/", "-").Replace("+", "_").Replace("=", "")
End Function

Function Base64ToGuid(ByVal base64 As String) As Guid
    Dim guid As Guid
    base64 = base64.Replace("-", "/").Replace("_", "+") & "=="

    Try
        guid = New Guid(Convert.FromBase64String(base64))
    Catch ex As Exception
        Throw New Exception("Bad Base64 conversion to GUID", ex)
    End Try

    Return guid
End Function

C#

public string GuidToBase64(Guid guid)
{
    return Convert.ToBase64String(guid.ToByteArray()).Replace("/", "-").Replace("+", "_").Replace("=", "");
}

public Guid Base64ToGuid(string base64)
{
   Guid guid = default(Guid);
   base64 = base64.Replace("-", "/").Replace("_", "+") + "==";

   try {
       guid = new Guid(Convert.FromBase64String(base64));
   }
   catch (Exception ex) {
       throw new Exception("Bad Base64 conversion to GUID", ex);
   }

   return guid;
}

0 Stimmen

Gibt es einen besonderen Grund für die Entfernung der Standard-Sonderzeichen der Base64-Kodierung?

0 Stimmen

Gibt es einen bestimmten Grund, warum Sie es kodieren müssen? Keines der Zeichen in einer GUID muss für URLs oder Attribute kodiert werden.

0 Stimmen

@Hemant, weil für URL, + und / und = funktioniert nicht gut in einem GET, @blowdart, um die url kleiner zu machen

33voto

JamesBrownIsDead Punkte 521

Vielleicht sollten Sie sich diese Website ansehen: http://prettycode.org/2009/11/12/short-guid/

Es sieht sehr ähnlich aus wie das, was Sie machen.

public class ShortGuid
{
    private readonly Guid guid;
    private readonly string value;

    /// <summary>Create a 22-character case-sensitive short GUID.</summary>
    public ShortGuid(Guid guid)
    {
        if (guid == null)
        {
            throw new ArgumentNullException("guid");
        }

        this.guid = guid;
        this.value = Convert.ToBase64String(guid.ToByteArray())
            .Substring(0, 22)
            .Replace("/", "_")
            .Replace("+", "-");
    }

    /// <summary>Get the short GUID as a string.</summary>
    public override string ToString()
    {
        return this.value;
    }

    /// <summary>Get the Guid object from which the short GUID was created.</summary>
    public Guid ToGuid()
    {
        return this.guid;
    }

    /// <summary>Get a short GUID as a Guid object.</summary>
    /// <exception cref="System.ArgumentNullException"></exception>
    /// <exception cref="System.FormatException"></exception>
    public static ShortGuid Parse(string shortGuid)
    {
        if (shortGuid == null)
        {
            throw new ArgumentNullException("shortGuid");
        }
        else if (shortGuid.Length != 22)
        {
            throw new FormatException("Input string was not in a correct format.");
        }

        return new ShortGuid(new Guid(Convert.FromBase64String
            (shortGuid.Replace("_", "/").Replace("-", "+") + "==")));
    }

    public static implicit operator String(ShortGuid guid)
    {
        return guid.ToString();
    }

    public static implicit operator Guid(ShortGuid shortGuid)
    {
        return shortGuid.guid;
    }
}

0 Stimmen

Ich verstehe, dass diese Antwort schon ziemlich alt ist, aber funktioniert Ihr Code tatsächlich? Das String-Ergebnis, das durch Guid.ToByteArray() + Byte-zu-Hex-Konvertierung erzeugt wird, wäre anders als der Aufruf von Guid.ToString(). Gemäß der .Net-Dokumentation Link : die Reihenfolge der Bytes im zurückgegebenen Byte-Array unterscheidet sich von der String-Darstellung eines Guid-Werts

24voto

Joe Punkte 117971

Ein Problem bei der Verwendung dieser Technik zur Formatierung einer GUID für die Verwendung in einer URL oder einem Dateinamen ist, dass zwei verschiedene GUIDs zwei Werte ergeben können, die sich nur in der Groß-/Kleinschreibung unterscheiden, z. B.:

    var b1 = GuidToBase64(new Guid("c9d045f3-e21c-46d0-971d-b92ebc2ab83c"));
    var b2 = GuidToBase64(new Guid("c9d045f3-e21c-46d0-971d-b92ebc2ab8a4"));
    Console.WriteLine(b1);  // 80XQyRzi0EaXHbkuvCq4PA
    Console.WriteLine(b2);  // 80XQyRzi0EaXHbkuvCq4pA

Da URLs manchmal so interpretiert werden, dass Groß- und Kleinschreibung nicht beachtet wird, und in Windows Dateipfade und Dateinamen Groß- und Kleinschreibung nicht beachten, kann dies zu Kollisionen führen.

6 Stimmen

Dies ist bei URLs nicht korrekt. Nur das Schema und der Host sollten als unabhängig von der Groß-/Kleinschreibung betrachtet werden, wie in RFC 3986 . Bei Abfrage, Fragment und sogar Pfad sollte die Groß- und Kleinschreibung beachtet werden. Natürlich hängt es ganz von Ihrem Code/Ihrer Server-Implementierung ab, ob Sie sich daran halten.

3 Stimmen

@RogerSpurrell - guter Punkt re: URLs, aber ich dachte mehr der anwendungsspezifischen Handhabung einer URL wie http://.../user/{id} , donde {id} könnte eine zufällige guid-ähnliche ID sein, um die OWASP Brute Force Predictable Resource Location-Schwachstelle zu vermeiden, und die ID könnte in einer Datenbank nachgeschlagen werden, die Groß- und Kleinschreibung nicht berücksichtigt.

22voto

Hemant Punkte 18906

Ich verstehe, dass der Grund, warum Sie sind Clipping == am Ende ist, dass, weil Sie sicher sein können, dass für GUID (von 16 Byte), kodierte Zeichenfolge wird immer enden mit ==. So können bei jeder Umwandlung 2 Zeichen eingespart werden.

Abgesehen von dem Punkt, den @Skurmedal bereits erwähnt hat (sollte eine Exception auslösen, wenn eine ungültige Zeichenkette eingegeben wird), denke ich, dass der von Ihnen gepostete Code gerade gut genug ist.

0 Stimmen

Daran habe ich gar nicht gedacht, eine clevere Platzersparnis, wenn man darüber nachdenkt :)

0 Stimmen

Was wäre am besten, Umgang mit einer Ausnahme oder Abfragen der Datenbank sowieso mit etwas, das nicht vorhanden ist? würde es mehr Code am Ende hinzufügen, da ich überprüfen, ob es mindestens eine Zeile im Ergebnis ist?

0 Stimmen

Es geht nur um wobei Sie den Scheck ausstellen wollen. Meiner Erfahrung nach sollten Low-Level-Bibliotheksroutinen so transparent wie möglich sein. Natürlich können Sie hier am besten beurteilen, wo der Fehlerprüfungscode hingehört, denn Sie Ihr Produkt kennen und wissen, wo diese Bibliothek/dieser Code steht. Das war nur ein Punkt zum Nachdenken.

4voto

Misha Zaslavsky Punkte 6203

Sur .NET Core können Sie Spannen für bessere Leistung und ohne Speicherzuweisung.

using System.Buffers.Text;
using System.Runtime.InteropServices;

namespace Extensions;

public static class GuidExtensions
{
    private const char Dash = '-';
    private const char EqualsChar = '=';
    private const byte ForwardSlashByte = (byte)Slash;
    private const char Plus = '+';
    private const byte PlusByte = (byte)Plus;
    private const char Slash = '/';
    private const char Underscore = '_';
    private const int Base64LengthWithoutEquals = 22;

    public static string EncodeBase64String(this Guid guid)
    {
        Span<byte> guidBytes = stackalloc byte[16];
        Span<byte> encodedBytes = stackalloc byte[24];

        MemoryMarshal.TryWrite(guidBytes, ref guid);
        Base64.EncodeToUtf8(guidBytes, encodedBytes, out _, out _);

        Span<char> chars = stackalloc char[Base64LengthWithoutEquals];

        // Replace any characters which are not URL safe.
        // And skip the final two bytes as these will be '==' padding we don't need.
        for (int i = 0; i < Base64LengthWithoutEquals; i++)
        {
            chars[i] = encodedBytes[i] switch
            {
                ForwardSlashByte => Dash,
                PlusByte => Underscore,
                _ => (char)encodedBytes[i],
            };
        }

        return new(chars);
    }

    public static Guid DecodeBase64String(this ReadOnlySpan<char> id)
    {
        Span<char> base64Chars = stackalloc char[24];

        for (var i = 0; i < Base64LengthWithoutEquals; i++)
        {
            base64Chars[i] = id[i] switch
            {
                Dash => Slash,
                Underscore => Plus,
                _ => id[i],
            };
        }

        base64Chars[22] = EqualsChar;
        base64Chars[23] = EqualsChar;

        Span<byte> idBytes = stackalloc byte[16];
        Convert.TryFromBase64Chars(base64Chars, idBytes, out _);

        return new(idBytes);
    }
}

using AutoFixture.Xunit2;
using FluentAssertions;
using Extensions;
using Xunit;

namespace ExtensionTests;

public class GuidExtensionsTests
{
    private const int Base64LengthWithoutEquals = 22;
    private const string EmptyBase64 = "AAAAAAAAAAAAAAAAAAAAAA";

    [Theory]
    [AutoData]
    public void EncodeBase64String_DecodeBase64String_Should_ReturnInitialGuid(Guid guid)
    {
        string actualBase64 = guid.EncodeBase64String();
        actualBase64.Should().NotBe(string.Empty)
            .And.HaveLength(Base64LengthWithoutEquals);

        Guid actualGuid = ((ReadOnlySpan<char>)actualBase64).DecodeBase64String();
        actualGuid.Should().Be(guid);
    }

    [Theory]
    [InlineData(EmptyBase64)]
    public void EncodeBase64String_Should_ReturnEmptyBase64_When_GuidIsEmpty(string expected)
    {
        string actualBase64 = Guid.Empty.EncodeBase64String();
        actualBase64.Should().Be(expected);
    }

    [Theory]
    [InlineData(EmptyBase64)]
    public void DecodeBase64String_Should_ReturnEmptyGuid_When_StringIsEmptyBase64(string base64)
    {
        Guid actual = ((ReadOnlySpan<char>)base64).DecodeBase64String();
        actual.Should().Be(Guid.Empty);
    }
}

Für weitere Informationen lesen Sie über die Verwendung von Hochleistungstechniken zur Base64-Kodierung einer Guid und eine sehr schöne Video-Erklärung .

3voto

Skurmedel Punkte 20549

Wenn Ihre Methode die ihr übergebene Base64 nicht in eine GUID umwandeln kann, sollten Sie dann nicht eine Ausnahme auslösen? Die an die Methode übergebenen Daten sind eindeutig fehlerhaft.

0 Stimmen

Ich glaube, ich stimme mit euch überein, über das Werfen einer Ausnahme, es macht mehr Sinn

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X