第1版第1/2/3/4次印刷(部分錯誤已經在第3/4次)
[上冊]
- P6 最后1段
- 原文:KestrelServer是采用libuv創建的跨平台Web服務器。
- 改為:KestrelServer是一款跨平台Web服務器。
- P8 第1段
- 原文:注冊的KestrelServer會綁定到“http//localhost:5000”和“https//localhost:5001”這兩個地址監聽請求
- 改為:注冊的KestrelServer會綁定到“http://localhost:5000”和“https://localhost:5001”這兩個地址監聽請求
- P14 第1段
- 原文:這兩個終結點通過預先設置的規則將具有某些特征的請求(如路徑、HTTP方法等)映射到對應的終結點
- 改為:這兩個中間件通過預先設置的規則將具有某些特征的請求(如路徑、HTTP方法等)映射到對應的終結點
- P21 第1段
- 原文:curl.exe or the Invoke-WebRequest
- 改為: curl或者Invoke-WebRequest
- P25 第1個代碼片段
- 原文:
- 改為
- P25 第1段
- 原文:具體來說,這個層將“microsoft/aspnetcore-build:2”作為基礎鏡像
- 改為:具體來說,這個層將“mcr.microsoft.com/dotnet/core/sdk:3.0 ”作為基礎鏡像
- P30 第4段
- 原文:除在運行的時候介紹內存占用外
- 改為:除在運行的時候減少內存占用外
- P47 第1段
- 原文:可以發現它具有如下兩個程序集的應用
- 改為:可以發現它具有如下針對兩個程序集的引用
- P51 第3段
- 原文:之前解決程序集服務的方案就是PCL
- 改為:之前解決程序集復用的方案就是PCL
- P51 第3段
- 原文:對於全新的 .NET 平台來說
- 改為:對於全新的 .NET Core平台來說
- P64 第2段
- 原文:這個方法可以是一個單純的虛方法
- 改為:這個方法可以是一個單純的抽象方法
- P67 第2個代碼片段
- 原文:public class FoobarEngineFactory : EngineFactory
- 改為:public class FoobarEngineFactory : MvcEngineFactory
- P69 第2個代碼片段
- 原文:.Register<ControllerActivator, SingletonControllerActivator>();
- 改為:.Register<IControllerActivator, SingletonControllerActivator>();
- P71 第1個代碼片段
- 原文: public Foo(IBar bar, IBaz):this(bar) =>Baz = baz;
- 改為:public Foo(IBar bar, IBaz baz):this(bar) =>Baz = baz;
- P73 第4段
- 原文:所以采用依賴注入模式的應用可以看作將服務推送到依賴注入容器
- 改為:所以采用依賴注入模式的應用可以看作將服務推送給被依賴對象
- P76 第1個代碼片段
- 原文
- 改為
# 5.2. 設置(運行)工作目錄,並將發布文件復制到out子目錄下 WORKDIR /app COPY --from= build /app/out .
# 5.2. 設置(運行)工作目錄,並將發布文件復制到out子目錄下 WORKDIR /app COPY --from=build /app/out .
public class Foobar<T1, T2>: IFoobar<T1,T2> { public IFoo Foo { get; } public IBar Bar { get; } public Foobar(IFoo foo, IBar bar) { Foo = foo; Bar = bar; } }
public class Foobar<T1, T2>: IFoobar<T1,T2> { public T1 Foo { get; } public T2 Bar { get; } public Foobar(T1 foo, T2 bar) { Foo = foo; Bar = bar; } }
- P91 第1個代碼片段
- 原文
- 改為
public class Foobar<T1, T2>: IFoobar<T1,T2> { public IFoo Foo { get; } public IBar Bar { get; } public Foobar(IFoo foo, IBar bar) { Foo = foo; Bar = bar; } }
public class Foobar<T1, T2>: IFoobar<T1,T2> { public T1 Foo { get; } public T2 Bar { get; } public Foobar(T1 foo, T2 bar) { Foo = foo; Bar = bar; } }
- P118 第1個代碼片段
- 原文: Debug.Assert(ReferenceEquals(rootScope, singletonService.ApplicationServices));
- 改為:Debug.Assert(ReferenceEquals(serviceProvider , singletonService.ApplicationServices));
- P127 第1個代碼片段
- 原文:
- 改為:
- P144 倒數第2段
- 原文:這兩個特殊的FileProvider類型都定義在“Microsoft.Extensions.FileProviders.Abstractions”這個NuGet包中。
- 改為:這兩個特殊的FileProvider類型分別定義在“Microsoft.Extensions.FileProviders.Abstractions”和“Microsoft.Extensions.FileProviders.Composite”NuGet包中。
- P147 倒數第2段
- 原文:所以它們分別映射為文件服務器上的目錄“c:\dir1”和“c:\dir1\foobar”。
- 改為:所以它們分別映射為文件服務器上的目錄“c:\test\dir1”和“c:\test\dir1\foobar”。
- P196 第2段
- 原文:FileConfigurationSource對象的Optional屬性表示當前配置源是否可以默認。如果該屬性被設置成False,即使指定的配置文件不存在也不會拋出異常。可默認的配置文件在支持多環境的場景中具有廣泛應用。正如前面的演示實例,我們可以按照如下方式加載兩個配置文件:基礎配置文件appsettings.json一般包含相對全面的配置,針對某個環境的差異化配置則定義在appsettings.{environment}.json文件中。前者是必需的,后者則是可以默認的,這保證了應用程序在缺少基於當前環境的差異化配置文件的情況下依然可以使用定義在基礎配置文件中的默認配置。
- 改為:FileConfigurationSource對象的Optional屬性表示當前配置源是否可以缺省。如果該屬性被設置成True,即使指定的配置文件不存在也不會拋出異常。可缺省的配置文件在支持多環境的場景中具有廣泛應用。正如前面的演示實例,我們可以按照如下方式加載兩個配置文件:基礎配置文件appsettings.json一般包含相對全面的配置,針對某個環境的差異化配置則定義在appsettings.{environment}.json文件中。前者是必需的,后者則是可以缺省的,這保證了應用程序在缺少基於當前環境的差異化配置文件的情況下依然可以使用定義在基礎配置文件中的默認配置。
- P213 第1段
- 原文:Lood方法還會利用這個DbContext對象將提供的初始化配置添加到數據庫中。
- 改為:Load方法還會利用這個DbContext對象將提供的初始化配置添加到數據庫中。
- P213 第1個代碼片段
- 原文
- 改為
- P222
- 原文:那么調用方法時就需要指定一個Action<TOptions,String>類型的委托對象,該委托對象的第二個參數表示Options的名稱
- 改為:那么調用方法時除了指定一個一個Action<TOptions>對象之外,還需要指定Options的名稱。
- P226第2段
- 原文:我們演示的實例已經涉及Options模型的3個重要的接口,它們分別是IOptions<TOptions>和IOptionsSnapshot<TOptions>
- 改為:我們演示的實例已經涉及Options模型的3個重要的接口,它們分別是IOptions<TOptions>、IOptionsSnapshot<TOptions>和IOptionsMonitor<TOptions>
- P233 第1段
- 原文:第二個反省參數代表依賴的服務類型
- 改為:第二個泛型參數代表依賴的服務類型
- P279 最后1段
- 原文:TraceListener具有兩個名為TraceData的方法
- 改為:TraceSource具有兩個名為TraceData的方法
- P299 最后1段
- 原文:宿主元素為通過Foobar對象轉換而成的EventPayload對象
- 改為:數組元素為通過Foobar對象轉換而成的EventPayload對象
- P309 第2個代碼片段
- 原文:
- 改為
- P311第1段
- 原文:它采用與演示實例提供的Observer<T>一樣的實現方式,即通過指定的委托對象(類型分別為Action<T>和Action<Exception>)實現IObservable<T>接口的3個方法
- 改為:它采用與演示實例提供的Observer<T>一樣的實現方式,即通過指定的委托對象(類型分別為Action<T>和Action<Exception>)實現IObserver<T>接口的3個方法
- P345 第1個代碼片段
- 原文
[ProviderAlias("Debug")] public class EventLogLoggerProvider : ILoggerProvider {...} [ProviderAlias("EventLog")] public class DebugLoggerProvider : ILoggerProvider {...}
- 改為
[ProviderAlias("Debug")] public class DebugLoggerProvider : ILoggerProvider {...} [ProviderAlias("EventLog")] public class EventLogLoggerProvider : ILoggerProvider {...}
- P386 第1個代碼片段
- 原文:"Host": "192.168.0.2" (appsettings.production.json)
- 改為:"Host": "192.168.0.3"
- P427 最后一個代碼片段
- 原文
class Program { static void Main() { Host.CreateDefaultBuilder() .ConfigureServices(svcs => svcs.AddSingleton( new StringContentMiddleware("Hello World!"))) .ConfigureWebHost(builder => builder .Configure(app => app.UseMiddleware<StringContentMiddleware>())) .Build() .Run(); } }
- 改為
class Program { static void Main() { Host.CreateDefaultBuilder() .ConfigureServices(svcs => svcs.AddSingleton( new StringContentMiddleware("Hello World!"))) .ConfigureWebHostDefaults(builder => builder .Configure(app => app.UseMiddleware<StringContentMiddleware>())) .Build() .Run(); }
- P432第1段
- 原文:我們從作為參數的ServiceCollection對象中獲取當前注冊的所有服務
- 改為:我們從作為參數的IServiceCollection對象中獲取當前注冊的所有服務
- 438第1段
- 原文:對於一個非根容器的IServiceProvider對象來說,其生命周期決定於對應的ServiceScope對象,調用ServiceScope的Dispose方法會導致對封裝IServiceProvider對象的回收釋放。
- 改為:對於一個非根容器的IServiceProvider對象來說,其生命周期決定於對應的IServiceScope對象,調用IServiceScope的Dispose方法會導致對封裝IServiceProvider對象的回收釋放。
- P451 第1個代碼片段
- 原文:
- 改為
foreach (var fileInfo in _fileProvider.GetDirectoryContents(subPath)) { render(indent, fileInfo.Name); if (fileInfo.IsDirectory) { Render($@"{subPath}\{fileInfo.Name}".TrimStart('\\')); } }
foreach (var fileInfo in _fileProvider.GetDirectoryContents(subPath)) { render(indent, fileInfo.Name); if (fileInfo.IsDirectory) { Render($@"{subPath}\{fileInfo.Name}".TrimStart('\\')); } } indent--;
... private IDictionary<string, string> Initialize( ApplicationSettingsContext dbContext) { foreach (var item in _initialSettings) { dbContext.Settings.Add(new ApplicationSetting(item.Key, item.Value)); } return _initialSettings.ToDictionary(it => it.Key, it => it.Value, StringComparer.OrdinalIgnoreCase); } }
private IDictionary<string, string> Initialize( ApplicationSettingsContext dbContext) { foreach (var item in _initialSettings) { dbContext.Settings.Add(new ApplicationSetting(item.Key, item.Value)); } dbContext.SaveChanges(); return _initialSettings.ToDictionary(it => it.Key, it => it.Value, StringComparer.OrdinalIgnoreCase); } }
public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer); public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Predicate<string> isEnabled); Func<string, object, object, bool> isEnabled);
public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer); public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Predicate<string> isEnabled); public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Func<string, object, object, bool> isEnabled);
public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configure) { var applicationName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name; ... }
public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configure) { var applicationName = configure.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name; ... }
- P457 第2段
- 原文:進而得到承載環境信息的IWebHostEnvironment服務,最終根據提供的環境信息進行有針對性的服務注冊
- 改為:進而得到承載環境信息的IWebHostEnvironment服務,最終根據提供的環境信息進行有針對性的中間件注冊
- P467 第1段
- 原文:本章將介紹真實的管道,而且會按照類似的設計重建一個Mini版的ASP.NET Core框架。
- 改為:本章不會介紹真實的管道,而是按照類似的設計重建一個Mini版的ASP.NET Core框架。
- P468 第1個代碼片段
- 原文:
- 改為:
- P472 第2段
- 原文:可以看出,IHttpRequestFeature接口和IHttpResponseFeature接口具有與抽象類型HttpRequest和HttpResponse完全一致的成員定義。
- 改為:可以看出,IHttpRequestFeature接口和IHttpResponseFeature接口具有與類型HttpRequest和HttpResponse完全一致的成員定義。
- P481 第2段
- 原文:也可以獲取代表請求的HTTP消息的首部和主題
- 改為:也可以獲取代表請求的HTTP消息的首部和主體
- P526 第1段
- 原文:定義在該程序集中的Startup方法會被加載出來
- 改為:定義在該程序集中的Startup類型會被加載出來
public class HttpContext { public abstract HttpRequest Request { get; } public abstract HttpResponse Response { get; } } public class HttpRequest { public abstract Uri Url { get; } public abstract NameValueCollection Headers { get; } public abstract Stream Body { get; } } public class HttpResponse { public abstract int StatusCode { get; set; } public abstract NameValueCollection Headers { get; } public abstract Stream Body { get; } }
public class HttpContext { public HttpRequest Request { get; } public HttpResponse Response { get; } } public class HttpRequest { public Uri Url { get; } public NameValueCollection Headers { get; } public Stream Body { get; } } public class HttpResponse { public int StatusCode { get; set; } public NameValueCollection Headers { get; } public Stream Body { get; } }
[下冊]
- 原文:第四個類型為IOptions<DirectoryBrowserOptions>的參數用於提供表示配置選項的DirectoryBrowserMiddleware的DirectoryBrowserOptions對象
- 改為:第四個類型為IOptions<DirectoryBrowserOptions>的參數用於提供表示配置選項的DirectoryBrowserOptions對象
- 原文:如果采用字符串“files/{name}.{ext?}”來表示針對某個文件的路由模板,文件名({name})和擴展名(ext?)體現為路由參數,而它們之間的“.”就是RoutePattern的第三種展現形式,被稱為分隔符。
- 改為:如果采用字符串“files/{name}.{ext?}”來表示針對某個文件的路由模板,文件名({name})和擴展名(ext?)體現為路由參數,而它們之間的“.”就是RoutePatternPart的第三種展現形式,被稱為分隔符。
- 原文:在這種狀況下就需要利用為注冊的路由模式指定不同的匹配的權重或者優先選擇一個匹配度最高的路由模式
- 改為:在這種狀況下就需要利用為注冊的路由模式指定不同的匹配的權重或者優先級選擇一個匹配度最高的路由模式
- 原文
- 改為
var newPath = new PathString(string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode)); var formatedQueryString = queryFormat == null ? null :string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode); context.HttpContext.Request.Path = newPath; context.HttpContext.Request.QueryString = newQueryString; await context.Next(context.HttpContext);
var newPath = new PathString(string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode)); var formatedQueryString = queryFormat == null ? null :string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode); var newQueryString = queryFormat == null ? QueryString.Empty : new QueryString(formatedQueryString); context.HttpContext.Request.Path = newPath; context.HttpContext.Request.QueryString = newQueryString; await context.Next(context.HttpContext);
- 原文:這個特性對應的接口是具有如下定義的IStatusCodeReExecuteFeature,但是該接口僅僅包含兩個針對路徑的屬性,並沒有用於攜帶原始請求上下文的屬性,但是默認實現類型StatusCodeReExecuteFeature包含了這個屬性。
- 改為:刪除該該內容(3.0中已經修復)
- 原文
public interface IStatusCodeReExecuteFeature { string OriginalPath { get; set; } string OriginalPathBase { get; set; } }
- 改為
public interface IStatusCodeReExecuteFeature { string OriginalPathBase { get; set; } string OriginalPath { get; set; } string OriginalQueryString { get; set; } }
- 原文:這是響應緩存的理論機制和指導思想
- 改為:這是響應緩存的理論基礎和指導思想
- 原文:一個Tick代表1納秒,即一千萬分之一秒,1 毫秒等於10 000 000納秒。DateTimeOffset的Ticks數返回距離“0001年1月1日午夜12:00:00”這個基准時間點的納秒數
- 改為:一個Tick代表100納秒,1納秒一千萬分之一秒,1 毫秒等於10 000 000納秒。DateTimeOffset的Ticks數返回距離“0001年1月1日午夜12:00:00”這個基准時間點的納秒數
- 原文:還需要提供一個LoggerFactory對象用來生成記錄日志的Logger,以及承載相關配置選項的ResponseCachingOptions對象。
- 改為:還需要提供一個ILoggerFactory工廠用來生成記錄日志的ILogger對象,以及承載相關配置選項的ResponseCachingOptions對象。
- 原文:一個ClaimsPrincipal對象還有一個必需的AuthenticationScheme屬性,該屬性表示采用的認證方案名稱
- 改為:一個AuthenticationTicket對象還有一個必需的AuthenticationScheme屬性,該屬性表示采用的認證方案名稱
- 原文:通過第9章的介紹可知,認證處理器是整個認證系統的核心,授權處理器也是整個授權系統的核心
- 改為:通過第19章的介紹可知,認證處理器是整個認證系統的核心,授權處理器也是整個授權系統的核心
- 刪除_culture字段相關代碼,整個DictionaryStringLocalizer 類型定義為
public class DictionaryStringLocalizer : IStringLocalizer { private readonly Dictionary<string, LocalizedStringEntry> _entries; public DictionaryStringLocalizer(Dictionary<string, LocalizedStringEntry> entries) => _entries = new Dictionary<string, LocalizedStringEntry>(entries); public LocalizedString this[string name] => GetString(name, CultureInfo.CurrentUICulture); public LocalizedString this[string name, params object[] arguments] { get { var raw = this[name]; return raw.ResourceNotFound ? raw : new LocalizedString(name, string.Format(raw.Value, arguments)); } } public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) { var culture = CultureInfo.CurrentUICulture; foreach (var item in _entries) { if (includeParentCultures) { yield return GetString(item.Key, culture); } else { yield return item.Value.Translations.TryGetValue(culture, out var text) ? new LocalizedString(item.Key, text) : new LocalizedString(item.Key, item.Key, true); } } } public IStringLocalizer WithCulture(CultureInfo culture) => throw new NotImplementedException(); private LocalizedString GetString(string name, CultureInfo culture) { if (!_entries.TryGetValue(name, out var entry)) { return new LocalizedString(name, name, true); } if (entry.Translations.TryGetValue(culture, out var message)) { return new LocalizedString(name, message); } if (culture == CultureInfo.InvariantCulture) { return new LocalizedString(name, entry.Value, true); } return GetString(name, culture.Parent); } }
- 原文:狀態為Unhealthy的服務被視為不可用(Unavailable),所以響應狀態碼為“Service Unavailable”
- 改為:狀態為Unhealthy的服務被視為不可用(Unavailable),所以響應狀態碼為“503 Service Unavailable”
- 原文:但是不能通過狀態碼來區分 Healthy 和 Unhealthy 這兩種可用狀態
- 改為:但是不能通過狀態碼來區分 Healthy 和 Degraded 這兩種可用狀態
- 原文:AddCheck<T>方法和GetServiceOrCreateInstance<T>方法的差異體現在它們利用依賴注入框架提供對應IHealthCheck對象的方式上。如果直接將IHealthCheck接口的實現類型或者實例注冊到依賴注入框架中,AddCheck<T>方法調用的是ActivatorUtilities類型的GetServiceOrCreateInstance<T>方法,意味着它會復用現有的Scoped服務條例或者Singleton服務實例。GetServiceOrCreateInstance<T>方法調用的是ActivatorUtilities類型的CreateInstance<T>方法,意味着它總是創建一個新的IHealthCheck對象。
- 改為:AddCheck<T>方法和AddTypeActivatedCheck<T>方法的差異體現在它們利用依賴注入框架提供對應IHealthCheck對象的方式上。如果直接將IHealthCheck接口的實現類型或者實例注冊到依賴注入框架中,AddCheck<T>方法調用的是ActivatorUtilities類型的GetServiceOrCreateInstance<T>方法,意味着它會復用現有的Scoped服務實例或者Singleton服務實例。AddTypeActivatedCheck<T>方法調用的是ActivatorUtilities類型的CreateInstance<T>方法,意味着它總是創建一個新的IHealthCheck對象。
- 原文:如下所示的內部DefaultHealthCheckService行繼承了抽象類HealthCheckService。
- 改為:如下所示的內部DefaultHealthCheckService類型繼承了抽象類HealthCheckService。
- 原文:我們需要先了解通過HostFilteringOptions類型標識的配置選項。
- 改為:我們需要先了解通過HostFilteringOptions類型表示的配置選項。
- 原文:
- 改為
Remote IP:192.168.0.1 Host:www.foo.com Scheme:http X-Original-For:[::1]:54578 X-Original-Host:localhost:5000 X-Original-Proto:http
Remote IP:192.168.0.254 Host:www.baz.com Scheme:https X-Original-For:[::1]:53754 X-Original-Host:localhost:5000 X-Original-Proto:http