Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate EntityLocalizationMiddleware #334

Merged
merged 1 commit into from
Feb 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ On selection of a language in the above code, the AccountController.SetLanguage
}
// Update PAL setting so that new language is reflected in any URL patched in the
// response (Late URL Localization).
HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
HttpContext.SetPrincipalAppLanguageForRequest(lt);
// Patch in the new langtag into any return URL.
if (returnUrl.IsSet()) {
returnUrl = LocalizedApplication.Current.UrlLocalizerForApp.SetLangTagInUrlPath(HttpContext, returnUrl, UriKind.RelativeOrAbsolute, lt == null ? null : lt.ToString()).ToString(); }
Expand Down Expand Up @@ -920,7 +920,8 @@ i18n.LocalizedApplication.Current.AsyncPostbackTypesToTranslate = "updatePanel,s

### OWIN support

Support for OWIN is available to a limited extent. See [Issue #241](https://github.com/turquoiseowl/i18n/issues/241) for more details.
Support for OWIN is available to a limited extent. See issues [#241](https://github.com/turquoiseowl/i18n/issues/241) and
[#333](https://github.com/turquoiseowl/i18n/issues/333) for more details.
i18n is created based on `HttpContextBase` in System.Web assembly, which means the foundation was built on IIS pipeline.
Currently we support OWIN hosted in IIS only, so it is still dependent on System.Web. Self-hosted OWIN is not supported.

Expand All @@ -929,7 +930,7 @@ Here is how to use i18n in OWIN Web API projects:
- Add reference to i18n.Adapter.OwinSystemWeb (available on NuGet as well)
- Add reference to Microsoft.Owin.Host.SystemWeb. If you add i18n.Adapter.OwinSystemWeb from NuGet it should automatically add this for you.
- No need to register HttpModule in web.config file.
- Add the following middleware registration into your startup sequence.
- Add the following middleware registration into your startup sequence:

```
public partial class Startup
Expand All @@ -938,17 +939,46 @@ public partial class Startup
{
...

// i18n middlewares
app.Use(typeof(i18n.Adapter.OwinSystemWeb.UrlLocalizationMiddleware));
app.Use(typeof(i18n.Adapter.OwinSystemWeb.EntityLocalizationMiddleware));

// i18n config
i18n.LocalizedApplication.Current.DefaultLanguage = "en";

// i18n middleware
app.Use(typeof(i18n.Adapter.OwinSystemWeb.UrlLocalizationMiddleware));

// i18n response filter installer for static files
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = (staticFileResponseContext) =>
{
if (staticFileResponseContext.File.Name.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
{
HttpContextBase context = staticFileResponseContext.OwinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
LocalizedApplication.InstallResponseFilter(context);
}
}
};
app.UseStaticFiles(staticFileOptions);

...
}
}
```

- Add the following handler to Global.asax:
```
/// <summary>
/// Handles the ReleaseRequestState event of the Application control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
protected void Application_ReleaseRequestState(object sender, EventArgs e)
{
HttpContextBase context = this.Request.GetOwinContext().Get<HttpContextBase>(typeof(HttpContextBase).FullName);
i18n.LocalizedApplication.InstallResponseFilter(context);
}
```


### A reminder about folders in a web application

Your `locale` folder is exposed to HTTP requests as-is, just like a typical log directory, so remember to block all requests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace i18n.Adapter.OwinSystemWeb
{
[Obsolete("This middleware is deprecated and should no longer be used. Refer to the documentation for more information.")]
public class EntityLocalizationMiddleware : OwinMiddleware
{
public EntityLocalizationMiddleware(OwinMiddleware next)
Expand All @@ -20,40 +21,7 @@ public async override Task Invoke(IOwinContext owinContext)
System.Web.HttpContextBase context = owinContext.Get<System.Web.HttpContextBase>(typeof(System.Web.HttpContextBase).FullName);
Debug.WriteLine("OwinMiddleware::Invoke -- ContentType: {0},\n\tUrl: {1}\n\tRawUrl:{2}", context.Response.ContentType, context.Request.Url, context.Request.RawUrl);

// If the content type of the entity is eligible for processing AND the URL is not to be excluded,
// wire up our filter to do the processing. The entity data will be run through the filter a
// bit later on in the pipeline.
if ((LocalizedApplication.Current.ContentTypesToLocalize != null
&& LocalizedApplication.Current.ContentTypesToLocalize.Match(context.Response.ContentType).Success) // Include certain content types from being processed
)
{
if ((LocalizedApplication.Current.UrlsToExcludeFromProcessing != null
&& LocalizedApplication.Current.UrlsToExcludeFromProcessing.Match(context.Request.RawUrl).Success) // Exclude certain URLs from being processed
)
{
Debug.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, URL excluded: ({0}).", context.Request.RawUrl);
}
else if ((context.Response.Headers["Content-Encoding"] != null
|| context.Response.Headers["Content-Encoding"] == "gzip") // Exclude responses that have already been compressed earlier in the pipeline
)
{
Debug.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, response compressed.");
}
else
{
var rootServices = LocalizedApplication.Current.RootServices;
Debug.WriteLine("LocalizingModule::OnReleaseRequestState -- Installing filter");
context.Response.Filter = new ResponseFilter(
context,
context.Response.Filter,
UrlLocalizer.UrlLocalizationScheme == UrlLocalizationScheme.Void ? null : rootServices.EarlyUrlLocalizerForApp,
rootServices.NuggetLocalizerForApp);
}
}
else
{
Debug.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, No content-type match: ({0}).", context.Response.ContentType);
}
LocalizedApplication.InstallResponseFilter(context);

await Next.Invoke(owinContext);
}
Expand Down
61 changes: 57 additions & 4 deletions src/i18n/LocalizedApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class DefaultRootServices : IRootServices
public DefaultRootServices()
{
// Use Lazy to delay the creation of objects to when a request is being processed.
// When initializing the app thehi may throw "Request is not available in this context" from WebConfigService
// When initializing the app thehi may throw "Request is not available in this context" from WebConfigService
translationRepository = new Lazy<POTranslationRepository>(() => new POTranslationRepository(new i18nSettings(new WebConfigSettingService())));
urlLocalizer = new UrlLocalizer();
textLocalizer = new Lazy<TextLocalizer>(() => new TextLocalizer(new i18nSettings(new WebConfigSettingService()), TranslationRepositoryForApp));
Expand Down Expand Up @@ -173,7 +173,7 @@ public string DefaultLanguage
/// language (PAL) is set for an HTTP request.
/// </summary>
/// <remarks>
/// A default handlers is installed which applies the PAL setting to both the
/// A default handlers is installed which applies the PAL setting to both the
/// CurrentCulture and CurrentUICulture settings of the current thread.
/// This behaviour can be altered by removing (nulling) the value of this property
/// or replacing with a new delegate.
Expand All @@ -185,7 +185,7 @@ public string DefaultLanguage
/// that allows the resulting message to be modified.
/// </summary>
/// <remarks>
/// In general it is good practice to postpone the escaping of characters until they
/// In general it is good practice to postpone the escaping of characters until they
/// are about to be displayed and then according to the content type of the output.
/// Thus, a single quote character need not be escaped if in JSON, but should be escaped
/// if in HTML or Javascript.
Expand All @@ -205,7 +205,7 @@ public string DefaultLanguage
/// that allows the resulting message to be modified.
/// </summary>
/// <remarks>
/// In general it is good practice to postpone the escaping of characters until they
/// In general it is good practice to postpone the escaping of characters until they
/// are about to be displayed and then according to the content type of the output.
/// This, a single quote character need not be escaped if in JSON, but should be escaped
/// if in HTML or Javascript.
Expand Down Expand Up @@ -308,6 +308,59 @@ public static LocalizedApplication Current
set { current = value; }
}

/// <summary>
/// Conditionally installs the i18n response filter.
/// </summary>
/// <param name="context">The HttpContext context.</param>
public static void InstallResponseFilter(System.Web.HttpContextBase context)
{
InstallResponseFilter(context, null);
}

/// <summary>
/// Conditionally installs the i18n response filter.
/// </summary>
/// <param name="context">The HTTP context.</param>
/// <param name="rootServices">The root services.</param>
public static void InstallResponseFilter(System.Web.HttpContextBase context, IRootServices rootServices)
{
if (rootServices == null)
{
rootServices = Current.RootServices;
}

// If the content type of the entity is eligible for processing AND the URL is not to be excluded,
// wire up our filter to do the processing. The entity data will be run through the filter a
// bit later on in the pipeline.
if (Current.ContentTypesToLocalize != null
&& Current.ContentTypesToLocalize.Match(context.Response.ContentType).Success) // Include certain content types from being processed
{
if (Current.UrlsToExcludeFromProcessing != null
&& Current.UrlsToExcludeFromProcessing.Match(context.Request.RawUrl).Success) // Exclude certain URLs from being processed
{
DebugHelpers.WriteLine("InstallResponseFilter -- Bypassing filter, URL excluded: ({0}).", context.Request.RawUrl);
}
else if (context.Response.Headers["Content-Encoding"] != null
|| context.Response.Headers["Content-Encoding"] == "gzip") // Exclude responses that have already been compressed earlier in the pipeline
{
DebugHelpers.WriteLine("InstallResponseFilter -- Bypassing filter, response compressed.");
}
else
{
DebugHelpers.WriteLine("InstallResponseFilter -- Installing filter");
context.Response.Filter = new ResponseFilter(
context,
context.Response.Filter,
UrlLocalizer.UrlLocalizationScheme == UrlLocalizationScheme.Void ? null : rootServices.EarlyUrlLocalizerForApp,
rootServices.NuggetLocalizerForApp);
}
}
else
{
DebugHelpers.WriteLine("InstallResponseFilter -- Bypassing filter, No content-type match: ({0}).", context.Response.ContentType);
}
}

/// <summary>
/// This object relays its implementaion of IRootServices onto the object set here.
/// Host app may override with its own implementation.
Expand Down
44 changes: 6 additions & 38 deletions src/i18n/Pipeline/LocalizingModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ namespace i18n
/// </summary>
/// <remarks>
/// LocalizingModule can be installed like this:
///
///
/// IIS7+ Integrated mode:
///
///
/// &lt;system.webServer&gt;
/// &lt;modules&gt;
/// &lt;add name="i18n.LocalizingModule" type="i18n.LocalizingModule, i18n" /&gt;
/// &lt;/modules&gt;
/// &lt;/system.webServer&gt;
///
///
/// IIS7 Classic mode and IIS6:
///
///
/// &lt;system.web&gt;
/// &lt;httpModules&gt;
/// &lt;add name="i18n.LocalizingModule" type="i18n.LocalizingModule, i18n" /&gt; &lt;!-- #37 --&gt;
Expand All @@ -49,7 +49,7 @@ public LocalizingModule(
public void Init(System.Web.HttpApplication application)
{
DebugHelpers.WriteLine("LocalizingModule::Init -- application: {0}", application);

// Wire up our event handlers into the ASP.NET pipeline.
application.BeginRequest += OnBeginRequest;
application.ReleaseRequestState += OnReleaseRequestState;
Expand Down Expand Up @@ -92,42 +92,10 @@ private void OnBeginRequest(object sender, EventArgs e)
/// </summary>
private void OnReleaseRequestState(object sender, EventArgs e)
{
//
System.Web.HttpContextBase context = System.Web.HttpContext.Current.GetHttpContextBase();
DebugHelpers.WriteLine("LocalizingModule::OnReleaseRequestState -- sender: {0}, e:{1}, ContentType: {2},\n\tUrl: {3}\n\tRawUrl:{4}", sender, e, context.Response.ContentType, context.Request.Url, context.Request.RawUrl);

// If the content type of the entity is eligible for processing AND the URL is not to be excluded,
// wire up our filter to do the processing. The entity data will be run through the filter a
// bit later on in the pipeline.
if ((LocalizedApplication.Current.ContentTypesToLocalize != null
&& LocalizedApplication.Current.ContentTypesToLocalize.Match(context.Response.ContentType).Success) // Include certain content types from being processed
)
{
if ((LocalizedApplication.Current.UrlsToExcludeFromProcessing != null
&& LocalizedApplication.Current.UrlsToExcludeFromProcessing.Match(context.Request.RawUrl).Success) // Exclude certain URLs from being processed
)
{
DebugHelpers.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, URL excluded: ({0}).", context.Request.RawUrl);
}
else if ((context.Response.Headers["Content-Encoding"] != null
|| context.Response.Headers["Content-Encoding"] == "gzip") // Exclude responses that have already been compressed earlier in the pipeline
)
{
DebugHelpers.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, response compressed.");
}
else
{
DebugHelpers.WriteLine("LocalizingModule::OnReleaseRequestState -- Installing filter");
context.Response.Filter = new ResponseFilter(
context,
context.Response.Filter,
UrlLocalizer.UrlLocalizationScheme == UrlLocalizationScheme.Void ? null : m_rootServices.EarlyUrlLocalizerForApp,
m_rootServices.NuggetLocalizerForApp);
}
}
else {
DebugHelpers.WriteLine("LocalizingModule::OnReleaseRequestState -- Bypassing filter, No content-type match: ({0}).", context.Response.ContentType);
}
LocalizedApplication.InstallResponseFilter(context, m_rootServices);
}

private void OnPostRequestHandlerExecute(object sender, EventArgs e)
Expand Down