Convert the static UriHelper into a service, IUriHelper, and inject where needed

This commit is contained in:
Steve Sanderson 2018-02-23 10:35:28 +00:00
parent 68f6ede3a7
commit 82bcf9172a
8 changed files with 103 additions and 60 deletions

View File

@ -34,7 +34,7 @@ export const monoPlatform: Platform = {
const typeHandle = find_class(assemblyHandle, namespace, className); const typeHandle = find_class(assemblyHandle, namespace, className);
if (!typeHandle) { if (!typeHandle) {
throw new Error(`Could not find type "${className}'" in namespace "${namespace}" in assembly "${assemblyName}"`); throw new Error(`Could not find type "${className}" in namespace "${namespace}" in assembly "${assemblyName}"`);
} }
const methodHandle = find_method(typeHandle, methodName, -1); const methodHandle = find_method(typeHandle, methodName, -1);

View File

@ -1,7 +1,7 @@
import { registerFunction } from '../Interop/RegisteredFunction'; import { registerFunction } from '../Interop/RegisteredFunction';
import { platform } from '../Environment'; import { platform } from '../Environment';
import { MethodHandle } from '../Platform/Platform'; import { MethodHandle } from '../Platform/Platform';
const registeredFunctionPrefix = 'Microsoft.AspNetCore.Blazor.Browser.Routing.UriHelper'; const registeredFunctionPrefix = 'Microsoft.AspNetCore.Blazor.Browser.Services.BrowserUriHelper';
let notifyLocationChangedMethod: MethodHandle; let notifyLocationChangedMethod: MethodHandle;
let hasRegisteredEventListeners = false; let hasRegisteredEventListeners = false;
@ -37,8 +37,8 @@ function handleInternalNavigation() {
if (!notifyLocationChangedMethod) { if (!notifyLocationChangedMethod) {
notifyLocationChangedMethod = platform.findMethod( notifyLocationChangedMethod = platform.findMethod(
'Microsoft.AspNetCore.Blazor.Browser', 'Microsoft.AspNetCore.Blazor.Browser',
'Microsoft.AspNetCore.Blazor.Browser.Routing', 'Microsoft.AspNetCore.Blazor.Browser.Services',
'UriHelper', 'BrowserUriHelper',
'NotifyLocationChanged' 'NotifyLocationChanged'
); );
} }

View File

@ -7,6 +7,7 @@ using System.Reflection;
using Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.Layouts; using Microsoft.AspNetCore.Blazor.Layouts;
using Microsoft.AspNetCore.Blazor.RenderTree; using Microsoft.AspNetCore.Blazor.RenderTree;
using Microsoft.AspNetCore.Blazor.Services;
namespace Microsoft.AspNetCore.Blazor.Browser.Routing namespace Microsoft.AspNetCore.Blazor.Browser.Routing
{ {
@ -22,6 +23,8 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
string _baseUriPrefix; string _baseUriPrefix;
string _locationAbsolute; string _locationAbsolute;
[Inject] private IUriHelper UriHelper { get; set; }
/// <summary> /// <summary>
/// Gets or sets the assembly that should be searched, along with its referenced /// Gets or sets the assembly that should be searched, along with its referenced
/// assemblies, for components matching the URI. /// assemblies, for components matching the URI.

View File

@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.RenderTree; using Microsoft.AspNetCore.Blazor.RenderTree;
using Microsoft.AspNetCore.Blazor.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -32,6 +33,8 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
private string _hrefAbsolute; private string _hrefAbsolute;
private IReadOnlyDictionary<string, object> _allAttributes; private IReadOnlyDictionary<string, object> _allAttributes;
[Inject] private IUriHelper UriHelper { get; set; }
public void Init(RenderHandle renderHandle) public void Init(RenderHandle renderHandle)
{ {
_renderHandle = renderHandle; _renderHandle = renderHandle;

View File

@ -2,28 +2,31 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Browser.Interop; using Microsoft.AspNetCore.Blazor.Browser.Interop;
using Microsoft.AspNetCore.Blazor.Services;
using System; using System;
namespace Microsoft.AspNetCore.Blazor.Browser.Routing namespace Microsoft.AspNetCore.Blazor.Browser.Services
{ {
// TODO: Make this not static, and wrap it in an interface that can be injected through DI.
/// <summary> /// <summary>
/// Helpers for working with URIs and navigation state. /// Default browser implementation of <see cref="IUriHelper"/>.
/// </summary> /// </summary>
public static class UriHelper public class BrowserUriHelper : IUriHelper
{ {
static readonly string _functionPrefix = typeof(UriHelper).FullName; // Since there's only one browser (and hence only one navigation state), the internal state
// is all static. In typical usage the DI system will register BrowserUriHelper as a singleton
// so it makes no difference, but if you manually instantiate more than one BrowserUriHelper
// that's fine too - they will just share their internal state.
// This class will never be used during server-side prerendering, so we don't have thread-
// safety concerns due to the static state.
static readonly string _functionPrefix = typeof(BrowserUriHelper).FullName;
static bool _hasEnabledNavigationInterception;
static string _currentAbsoluteUri; static string _currentAbsoluteUri;
static EventHandler<string> _onLocationChanged;
static string _baseUriString; static string _baseUriString;
static Uri _baseUri; static Uri _baseUri;
static EventHandler<string> _onLocationChanged;
static bool _hasEnabledNavigationInterception;
/// <summary> /// <inheritdoc />
/// An event that fires when the navigation location has changed. public event EventHandler<string> OnLocationChanged
/// </summary>
public static event EventHandler<string> OnLocationChanged
{ {
add add
{ {
@ -38,63 +41,34 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
} }
} }
/// <summary> /// <inheritdoc />
/// Gets the URI prefix that can be prepended before URI paths to produce an absolute URI. public string GetBaseUriPrefix()
/// Typically this corresponds to the 'href' attribute on the document's &lt;base&gt; element.
/// </summary>
/// <returns>The URI prefix.</returns>
public static string GetBaseUriPrefix()
{ {
EnsureBaseUriPopulated(); EnsureBaseUriPopulated();
return _baseUriString; return _baseUriString;
} }
private static void EnsureBaseUriPopulated() /// <inheritdoc />
{ public string GetAbsoluteUri()
// The <base href> is fixed for the lifetime of the page, so just cache it
if (_baseUriString == null)
{
var baseUri = RegisteredFunction.InvokeUnmarshalled<string>(
$"{_functionPrefix}.getBaseURI");
_baseUriString = ToBaseUriPrefix(baseUri);
_baseUri = new Uri(_baseUriString);
}
}
/// <summary>
/// Gets the browser's current absolute URI.
/// </summary>
/// <returns>The browser's current absolute URI.</returns>
public static string GetAbsoluteUri()
{ {
if (_currentAbsoluteUri == null) if (_currentAbsoluteUri == null)
{ {
_currentAbsoluteUri = RegisteredFunction.InvokeUnmarshalled<string>( _currentAbsoluteUri = RegisteredFunction.InvokeUnmarshalled<string>(
$"{_functionPrefix}.getLocationHref"); $"{_functionPrefix}.getLocationHref");
} }
return _currentAbsoluteUri; return _currentAbsoluteUri;
} }
/// <summary> /// <inheritdoc />
/// Converts a relative URI into an absolute one. public Uri ToAbsoluteUri(string relativeUri)
/// </summary>
/// <param name="relativeUri">The relative URI.</param>
/// <returns>The absolute URI.</returns>
public static Uri ToAbsoluteUri(string relativeUri)
{ {
EnsureBaseUriPopulated(); EnsureBaseUriPopulated();
return new Uri(_baseUri, relativeUri); return new Uri(_baseUri, relativeUri);
} }
/// <summary> /// <inheritdoc />
/// Given a base URI prefix (e.g., one previously returned by <see cref="GetBaseUriPrefix"/>), public string ToBaseRelativePath(string baseUriPrefix, string absoluteUri)
/// converts an absolute URI into one relative to the base URI prefix.
/// </summary>
/// <param name="baseUriPrefix">The base URI prefix (e.g., previously returned by <see cref="GetBaseUriPrefix"/>).</param>
/// <param name="absoluteUri">An absolute URI that is within the space of the base URI prefix.</param>
/// <returns>A relative URI path.</returns>
public static string ToBaseRelativePath(string baseUriPrefix, string absoluteUri)
{ {
if (absoluteUri.Equals(baseUriPrefix, StringComparison.Ordinal)) if (absoluteUri.Equals(baseUriPrefix, StringComparison.Ordinal))
{ {
@ -117,6 +91,18 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
throw new ArgumentException($"The URI '{absoluteUri}' is not contained by the base URI '{baseUriPrefix}'."); throw new ArgumentException($"The URI '{absoluteUri}' is not contained by the base URI '{baseUriPrefix}'.");
} }
private static void EnsureBaseUriPopulated()
{
// The <base href> is fixed for the lifetime of the page, so just cache it
if (_baseUriString == null)
{
var baseUri = RegisteredFunction.InvokeUnmarshalled<string>(
$"{_functionPrefix}.getBaseURI");
_baseUriString = ToBaseUriPrefix(baseUri);
_baseUri = new Uri(_baseUriString);
}
}
private static void NotifyLocationChanged(string newAbsoluteUri) private static void NotifyLocationChanged(string newAbsoluteUri)
{ {
_currentAbsoluteUri = newAbsoluteUri; _currentAbsoluteUri = newAbsoluteUri;

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
@ -39,7 +40,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Services
private void AddDefaultServices(ServiceCollection serviceCollection) private void AddDefaultServices(ServiceCollection serviceCollection)
{ {
// TODO: Add default services for HttpClient, IUrlHelper, etc. serviceCollection.AddSingleton<IUriHelper>(new BrowserUriHelper());
} }
} }
} }

View File

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Blazor.Services
{
/// <summary>
/// Helpers for working with URIs and navigation state.
/// </summary>
public interface IUriHelper
{
/// <summary>
/// Gets the current absolute URI.
/// </summary>
/// <returns>The browser's current absolute URI.</returns>
string GetAbsoluteUri();
/// <summary>
/// An event that fires when the navigation location has changed.
/// </summary>
event EventHandler<string> OnLocationChanged;
/// <summary>
/// Converts a relative URI into an absolute one (by resolving it
/// relative to the current absolute URI).
/// </summary>
/// <param name="relativeUri">The relative URI.</param>
/// <returns>The absolute URI.</returns>
Uri ToAbsoluteUri(string href);
/// <summary>
/// Gets the URI prefix that can be prepended before URI paths to produce an absolute URI.
/// Typically this corresponds to the 'href' attribute on the document's &lt;base&gt; element.
/// </summary>
/// <returns>The URI prefix.</returns>
string GetBaseUriPrefix();
/// <summary>
/// Given a base URI prefix (e.g., one previously returned by <see cref="GetBaseUriPrefix"/>),
/// converts an absolute URI into one relative to the base URI prefix.
/// </summary>
/// <param name="baseUriPrefix">The base URI prefix (e.g., previously returned by <see cref="GetBaseUriPrefix"/>).</param>
/// <param name="absoluteUri">An absolute URI that is within the space of the base URI prefix.</param>
/// <returns>A relative URI path.</returns>
string ToBaseRelativePath(string baseUriPrefix, string locationAbsolute);
}
}

View File

@ -1,14 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Browser.Routing; using Microsoft.AspNetCore.Blazor.Browser.Services;
using System; using System;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Blazor.Browser.Test namespace Microsoft.AspNetCore.Blazor.Browser.Test
{ {
public class UriHelperTest public class BrowserUriHelperTest
{ {
private BrowserUriHelper _browserUriHelper = new BrowserUriHelper();
[Theory] [Theory]
[InlineData("scheme://host/", "scheme://host")] [InlineData("scheme://host/", "scheme://host")]
[InlineData("scheme://host:123/", "scheme://host:123")] [InlineData("scheme://host:123/", "scheme://host:123")]
@ -17,7 +19,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Test
[InlineData("scheme://host/path/page?query=string&another=here", "scheme://host/path")] [InlineData("scheme://host/path/page?query=string&another=here", "scheme://host/path")]
public void ComputesCorrectBaseUriPrefix(string baseUri, string expectedResult) public void ComputesCorrectBaseUriPrefix(string baseUri, string expectedResult)
{ {
var actualResult = UriHelper.ToBaseUriPrefix(baseUri); var actualResult = BrowserUriHelper.ToBaseUriPrefix(baseUri);
Assert.Equal(expectedResult, actualResult); Assert.Equal(expectedResult, actualResult);
} }
@ -29,7 +31,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Test
[InlineData("scheme://host/path", "scheme://host/path", "/")] [InlineData("scheme://host/path", "scheme://host/path", "/")]
public void ComputesCorrectValidBaseRelativePaths(string baseUriPrefix, string absoluteUri, string expectedResult) public void ComputesCorrectValidBaseRelativePaths(string baseUriPrefix, string absoluteUri, string expectedResult)
{ {
var actualResult = UriHelper.ToBaseRelativePath(baseUriPrefix, absoluteUri); var actualResult = _browserUriHelper.ToBaseRelativePath(baseUriPrefix, absoluteUri);
Assert.Equal(expectedResult, actualResult); Assert.Equal(expectedResult, actualResult);
} }
@ -40,7 +42,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Test
{ {
var ex = Assert.Throws<ArgumentException>(() => var ex = Assert.Throws<ArgumentException>(() =>
{ {
UriHelper.ToBaseRelativePath(baseUriPrefix, absoluteUri); _browserUriHelper.ToBaseRelativePath(baseUriPrefix, absoluteUri);
}); });
Assert.Equal( Assert.Equal(