zoukankan      html  css  js  c++  java
  • 依赖注入的威力,.NET Core的魅力:解决MVC视图中的中文被html编码的问题

    有园友在博问中提了这样一个问题 —— .NET Core 中文等非英文文字html编码输出问题,到我们的 ASP.NET Core 项目中一看,也是同样的问题。

    比如下面的Razor视图代码:

    @{
        ViewBag.Title = "代码改变世界";
    }
    <title>@ViewBag.Title</title>

    输出的html代码变成了:

    <title>&#x4EE3;&#x7801;&#x6539;&#x53D8;&#x4E16;&#x754C;</title>

    上面的 @ViewBag.Title 实际上等同于下面的代码:

    @Html.Raw(Html.Encode(ViewBag.Title))

    所以解决这个问题需要从ASP.NET Core MVC中的HtmlHelper下手(上面代码中Html的类型就是HtmlHelper)。

    从GitHub上签出MVC的源代码看看HtmlHelper.Encode()的实现:

    private readonly IHtmlGenerator _htmlGenerator;
    public string Encode(string value)
    {
        return _htmlGenerator.Encode(value);
    }

    实际调用的是IHtmlGenerator接口的Encode()方法,MVC中实现这个接口的是DefaultHtmlGenerator,其对应的Encode()实现代码如下:

    private readonly HtmlEncoder _htmlEncoder;
    public string Encode(string value)
    {
        return !string.IsNullOrEmpty(value) ? _htmlEncoder.Encode(value) : string.Empty;
    }

    原来真正干活的主角是HtmlEncoder,但它不是在MVC中实现的,而是在.NET Core Framework中实现的,命名空间是 System.Text.Encodings.Web 。

    写个.NET Core控制台程序直接调用HtmlEncoder看看是不是就是它惹的祸。

    public class Program
    {
        public static void Main(string[] args)
        {            
            Console.WriteLine(HtmlEncoder.Default.Encode("代码改变世界"));
        }
    }

    输出结果与MVC中是同样的问题。

    试试不用默认的HtmlEncoder实例(HtmlEncoder.Default),而是自己调用HtmlEncoder.Create()方法创建实例,这时发现了UnicodeRange参数类型。

    public static HtmlEncoder Create(params UnicodeRange[] allowedRanges);

    当使用UnicodeRanges.All作为参数创建HtmlEncoder实例时,问题就解决了。

    Console.WriteLine(HtmlEncoder.Create(UnicodeRanges.All).Encode("代码改变世界"));

    紧接着从GitHub上签出System.Text.Encodings.Web的源代码,看看HtmlEncoder.Default对应的HtmlEncode实例是如何被创建的:

    internal readonly static DefaultHtmlEncoder Singleton = new DefaultHtmlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin));

    原来用的是UnicodeRanges.BasicLatin,难怪中文会被编码,搞不懂为什么默认不用UnicodeRanges.All?

    知道了问题的原因,解决起来就不难了,只要我们以HtmlEncoder.Create(UnicodeRanges.All)创建HtmlEncoder实例,并替换掉MVC中所用的默认HtmlEncoder实例。那如何替换呢?

    回到MVC的源代码中,看看DefaultHtmlGenerator的实现,发现它的构造函数参数中有HtmlEncoder:

    public DefaultHtmlGenerator(
        IAntiforgery antiforgery,
        IOptions<MvcViewOptions> optionsAccessor,
        IModelMetadataProvider metadataProvider,
        IUrlHelperFactory urlHelperFactory,
        HtmlEncoder htmlEncoder,
        ClientValidatorCache clientValidatorCache)
    {
    }

    根据.NET从上到下、由内而外全面依赖注入的秉性,这个地方应该也是依赖注入的,我们只需注入一个新的HtmlEncoder实例即可,是不是这样呢?

    码上一行,你就知道。

    在 Startup.cs 的 ConfigureServices() 方法中添加下面的一行代码:

    services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));

    运行ASP.NET Core站点,输出结果如下:

    <title>代码改变世界</title>

    一行注入,立马解决。依赖注入的威力,.NET Core的魅力。

    更新1:根据 零度的火 的评论,更好的解决方法是

    services.Configure<WebEncoderOptions>(options =>
    {
          options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All);
    });

    更新2:后来发现更好的解决方法

    services.Configure<WebEncoderOptions>(options =>
        options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.BasicLatin,
            UnicodeRanges.CjkUnifiedIdeographs));
  • 相关阅读:
    11111 Generalized Matrioshkas
    Uva 442 Matrix Chain Multiplication
    Uva 10815 Andy's First Dictionary
    Uva 537 Artificial Intelligence?
    Uva 340 MasterMind Hints
    SCAU 9508 诸葛给我牌(水泥题)
    Uva 10420 List of Conquests(排序水题)
    Uva 409 Excuses, Excuses!
    10/26
    11/2
  • 原文地址:https://www.cnblogs.com/dudu/p/5879913.html
Copyright © 2011-2022 走看看