diff --git a/samples/LocalizationSample/Startup.cs b/samples/LocalizationSample/Startup.cs index 2183eaf3af..2e2b93bd8b 100644 --- a/samples/LocalizationSample/Startup.cs +++ b/samples/LocalizationSample/Startup.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Globalization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; @@ -20,10 +21,19 @@ namespace LocalizationSample public void Configure(IApplicationBuilder app, IStringLocalizer SR) { - var options = new RequestLocalizationMiddlewareOptions + var options = new RequestLocalizationOptions { // Set options here to change middleware behavior - + //SupportedCultures = new List + //{ + // new CultureInfo("en-US"), + // new CultureInfo("en-AU") + //}, + //SupportedUICultures = new List + //{ + // new CultureInfo("en-US"), + // new CultureInfo("en-AU") + //} }; app.UseRequestLocalization(options); diff --git a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs index 030e294361..12d62ead83 100644 --- a/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/AcceptLanguageHeaderRequestCultureStrategy.cs @@ -1,7 +1,6 @@ // 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.Globalization; using System.Linq; using Microsoft.AspNet.Http; using Microsoft.AspNet.Localization.Internal; @@ -13,7 +12,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of the Accept-Language header. /// - public class AcceptLanguageHeaderRequestCultureStrategy : IRequestCultureStrategy + public class AcceptLanguageHeaderRequestCultureStrategy : RequestCultureStrategy { /// /// The maximum number of values in the Accept-Language header to attempt to create a @@ -23,7 +22,7 @@ namespace Microsoft.AspNet.Localization public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var acceptLanguageHeader = httpContext.Request.GetTypedHeaders().AcceptLanguage; @@ -53,7 +52,14 @@ namespace Microsoft.AspNet.Localization var culture = CultureInfoCache.GetCultureInfo(language.Value); if (culture != null) { - return RequestCulture.GetRequestCulture(culture); + var requestCulture = RequestCulture.GetRequestCulture(culture); + + requestCulture = ValidateRequestCulture(requestCulture); + + if (requestCulture != null) + { + return requestCulture; + } } } } diff --git a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs index ac9a1b3089..ead04c8320 100644 --- a/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CookieRequestCultureStrategy.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the value of a cookie. /// - public class CookieRequestCultureStrategy : IRequestCultureStrategy + public class CookieRequestCultureStrategy : RequestCultureStrategy { private static readonly char[] _cookieSeparator = new[] { '|' }; private static readonly string _culturePrefix = "c="; @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Localization public string CookieName { get; set; } = DefaultCookieName; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var cookie = httpContext.Request.Cookies[CookieName]; @@ -33,7 +33,11 @@ namespace Microsoft.AspNet.Localization return null; } - return ParseCookieValue(cookie); + var requestCulture = ParseCookieValue(cookie); + + requestCulture = ValidateRequestCulture(requestCulture); + + return requestCulture; } /// diff --git a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs index 7ddee3f627..5a5c87a825 100644 --- a/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/CustomRequestCultureStrategy.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via the configured delegate. /// - public class CustomRequestCultureStrategy : IRequestCultureStrategy + public class CustomRequestCultureStrategy : RequestCultureStrategy { private readonly Func _strategy; @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Localization } /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { return _strategy(httpContext); } diff --git a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs index d3d7a65077..c383cf7117 100644 --- a/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Localization/IApplicationBuilderExtensions.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization([NotNull] this IApplicationBuilder builder) { - var options = new RequestLocalizationMiddlewareOptions(); + var options = new RequestLocalizationOptions(); return UseRequestLocalization(builder, options); } @@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Builder /// The . public static IApplicationBuilder UseRequestLocalization( [NotNull] this IApplicationBuilder builder, - [NotNull] RequestLocalizationMiddlewareOptions options) + [NotNull] RequestLocalizationOptions options) { return builder.UseMiddleware(options); } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs index 73cd046f50..44cc487a63 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureFeature.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Localization /// /// The that determined the request's culture information. /// If the value is null then no strategy was used and the request's culture was set to the value of - /// . + /// . /// IRequestCultureStrategy Strategy { get; } } diff --git a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs index eb96670263..9e71c133f4 100644 --- a/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/IRequestCultureStrategy.cs @@ -10,6 +10,14 @@ namespace Microsoft.AspNet.Localization /// public interface IRequestCultureStrategy { + /// + /// Implements the strategy to determine the culture of the given request. + /// + /// The for the request. + /// + /// The determined . + /// Returns null if the strategy couldn't determine a . + /// RequestCulture DetermineRequestCulture(HttpContext httpContext); } } diff --git a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs index 80a7ba7f1f..19c3c077b6 100644 --- a/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs +++ b/src/Microsoft.AspNet.Localization/QueryStringRequestCultureStrategy.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Localization /// /// Determines the culture information for a request via values in the query string. /// - public class QueryStringRequestCultureStrategy : IRequestCultureStrategy + public class QueryStringRequestCultureStrategy : RequestCultureStrategy { /// /// The key that contains the culture name. @@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Localization public string UIQueryStringKey { get; set; } = "ui-culture"; /// - public RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) + public override RequestCulture DetermineRequestCulture([NotNull] HttpContext httpContext) { var request = httpContext.Request; if (!request.QueryString.HasValue) @@ -67,7 +67,11 @@ namespace Microsoft.AspNet.Localization return null; } - return RequestCulture.GetRequestCulture(culture, uiCulture); + var requestCulture = RequestCulture.GetRequestCulture(culture, uiCulture); + + requestCulture = ValidateRequestCulture(requestCulture); + + return requestCulture; } } } diff --git a/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs new file mode 100644 index 0000000000..422f5d6f9e --- /dev/null +++ b/src/Microsoft.AspNet.Localization/RequestCultureStrategy.cs @@ -0,0 +1,61 @@ +// 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 Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Localization +{ + /// + /// An abstract base class strategy for determining the culture information of an . + /// + public abstract class RequestCultureStrategy : IRequestCultureStrategy + { + /// + /// The current options for the . + /// + public RequestLocalizationOptions Options { get; set; } + + /// + public abstract RequestCulture DetermineRequestCulture(HttpContext httpContext); + + /// + /// Determines if the given is valid according to the currently configured. + /// . + /// + /// The to validate. + /// + /// The original if it was valid, otherwise a new + /// with values for and that are + /// valid for the current configuration, or null if neither or + /// were valid. + /// + protected RequestCulture ValidateRequestCulture(RequestCulture requestCulture) + { + if (requestCulture == null || Options == null) + { + return requestCulture; + } + + var result = requestCulture; + + if (Options.SupportedCultures != null && !Options.SupportedCultures.Contains(result.Culture)) + { + result = RequestCulture.GetRequestCulture(Options.DefaultRequestCulture.Culture, result.UICulture); + } + + if (Options.SupportedUICultures != null && !Options.SupportedUICultures.Contains(result.UICulture)) + { + result = RequestCulture.GetRequestCulture(result.Culture, Options.DefaultRequestCulture.UICulture); + } + + if (requestCulture.Culture != result.Culture && requestCulture.UICulture != result.UICulture) + { + // Both cultures were invalid, just return null + return null; + } + + return result; + } + } +} diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs index 1cd4ea2be1..2ccfe10418 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationMiddleware.cs @@ -17,14 +17,14 @@ namespace Microsoft.AspNet.Localization public class RequestLocalizationMiddleware { private readonly RequestDelegate _next; - private readonly RequestLocalizationMiddlewareOptions _options; + private readonly RequestLocalizationOptions _options; /// /// Creates a new . /// /// The representing the next middleware in the pipeline. /// - public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationMiddlewareOptions options) + public RequestLocalizationMiddleware([NotNull] RequestDelegate next, [NotNull] RequestLocalizationOptions options) { _next = next; _options = options; @@ -56,20 +56,6 @@ namespace Microsoft.AspNet.Localization } } - if (_options.SupportedCultures != null) - { - // Ensure that selected cultures are in the supported list and if not, set them to the default - if (!_options.SupportedCultures.Contains(requestCulture.Culture)) - { - requestCulture = RequestCulture.GetRequestCulture(_options.DefaultRequestCulture.Culture, requestCulture.UICulture); - } - - if (!_options.SupportedUICultures.Contains(requestCulture.UICulture)) - { - requestCulture = RequestCulture.GetRequestCulture(requestCulture.Culture, _options.DefaultRequestCulture.UICulture); - } - } - context.SetFeature(new RequestCultureFeature(requestCulture, winningStrategy)); SetCurrentThreadCulture(requestCulture); diff --git a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs similarity index 82% rename from src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs rename to src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs index 0f24d18cf7..5279c98d8e 100644 --- a/src/Microsoft.AspNet.Localization/RequestLocalizationMiddlewareOptions.cs +++ b/src/Microsoft.AspNet.Localization/RequestLocalizationOptions.cs @@ -10,30 +10,23 @@ namespace Microsoft.AspNet.Localization /// /// Specifies options for the . /// - public class RequestLocalizationMiddlewareOptions + public class RequestLocalizationOptions { /// - /// Creates a new with default values. + /// Creates a new with default values. /// - public RequestLocalizationMiddlewareOptions() + public RequestLocalizationOptions() { DefaultRequestCulture = RequestCulture.GetRequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture); RequestCultureStrategies = new List { - new QueryStringRequestCultureStrategy(), - new CookieRequestCultureStrategy(), - new AcceptLanguageHeaderRequestCultureStrategy { MaximumAcceptLanguageHeaderValuesToTry = MaximumAcceptLanguageHeaderValuesToTry } + new QueryStringRequestCultureStrategy { Options = this }, + new CookieRequestCultureStrategy { Options = this }, + new AcceptLanguageHeaderRequestCultureStrategy { Options = this } }; } - /// - /// The maximum number of values in the Accept-Language header to attempt to create a - /// from for the current request. - /// Defaults to 3. - /// - public int MaximumAcceptLanguageHeaderValuesToTry { get; set; } = 3; - /// /// The default to use. This value will be used if none of the configured /// options result in a non-null result.