Skip to content

Commit

Permalink
Deprecate EntityLocalizationMiddleware
Browse files Browse the repository at this point in the history
Also, revert ca6e4f2 changes since HttpContext is a property in this context.
  • Loading branch information
macote committed Feb 6, 2017
1 parent 106cdf3 commit 414a6d2
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 83 deletions.
44 changes: 37 additions & 7 deletions README.md
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
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
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
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

0 comments on commit 414a6d2

Please sign in to comment.