通过这个我们知道ServerOptions来自于ServiceContext的ServerOptions属性,我们找到给ServiceContext赋值的地方,在KestrelServerImpl的CreateServiceContext方法里[点击查看源码👈]精简一下逻辑,抽出来核心内容大致实现如下
public KestrelServerImpl( IOptions<KestrelServerOptions> options, IEnumerable<IConnectionListenerFactory> transportFactories, ILoggerFactory loggerFactory) //注入进来的IOptions<KestrelServerOptions>调用了CreateServiceContext : this(transportFactories, null, CreateServiceContext(options, loggerFactory)) { } private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory) { //值来自于IOptions<KestrelServerOptions> var serverOptions = options.Value ?? new KestrelServerOptions(); return new ServiceContext { Log = trace, HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)), Scheduler = PipeScheduler.ThreadPool, SystemClock = heartbeatManager, DateHeaderValueManager = dateHeaderValueManager, ConnectionManager = connectionManager, Heartbeat = heartbeat, //赋值操作 ServerOptions = serverOptions, }; }
通过上面的代码我们可以看到如果配置了KestrelServerOptions那么ServiceContext的ServerOptions属性就来自于KestrelServerOptions,即我们通过services.Configure<KestrelServerOptions>()配置的值,总之得到了这么一个结论
如果配置了KestrelServerOptions即services.Configure(),那么AllowSynchronousIO来自于KestrelServerOptions。即IHttpBodyControlFeature的AllowSynchronousIO属性来自于KestrelServerOptions。如果没有配置,那么直接通过修改IHttpBodyControlFeature实例的
AllowSynchronousIO属性能得到相同的效果,毕竟HttpRequestStream是直接依赖的IHttpBodyControlFeature实例。
我们在上面的示例中看到了,如果不添加EnableBuffering的话直接设置RequestBody的Position会报NotSupportedException这么一个错误,而且加了它之后我居然可以直接使用同步的方式去读取RequestBody,首先我们来看一下为啥会报错,我们从上面的错误了解到错误来自于HttpRequestStream这个类[点击查看源码👈],上面我们也说了这个类继承了Stream抽象类,通过源码我们可以看到如下相关代码
//不能使用Seek操作 public override bool CanSeek => false; //允许读 public override bool CanRead => true; //不允许写 public override bool CanWrite => false; //不能获取长度 public override long Length => throw new NotSupportedException(); //不能读写Position public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } //不能使用Seek方法 public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
相信通过这些我们可以清楚的看到针对HttpRequestStream的设置或者写相关的操作是不被允许的,这也是为啥我们上面直接通过Seek设置Position的时候为啥会报错,还有一些其他操作的限制,总之默认是不希望我们对HttpRequestStream做过多的操作,特别是设置或者写相关的操作。但是我们使用EnableBuffering的时候却没有这些问题,究竟是为什么?接下来我们要揭开它的什么面纱了。首先我们从Request.EnableBuffering()这个方法入手,找到源码位置在HttpRequestRewindExtensions扩展类中[点击查看源码👈],我们从最简单的无参方法开始看到如下定义
/// <summary> /// 确保Request.Body可以被多次读取 /// </summary> /// <param></param> public static void EnableBuffering(this HttpRequest request) { BufferingHelper.EnableRewind(request); }
上面的方法是最简单的形式,还有一个EnableBuffering的扩展方法是参数最全的扩展方法,这个方法可以控制读取的大小和控制是否存储到磁盘的限定大小
/// <summary> /// 确保Request.Body可以被多次读取 /// </summary> /// <param></param> /// <param>内存中用于缓冲流的最大大小(字节)。较大的请求主体被写入磁盘。</param> /// <param>请求正文的最大大小(字节)。尝试读取超过此限制将导致异常</param> public static void EnableBuffering(this HttpRequest request, int bufferThreshold, long bufferLimit) { BufferingHelper.EnableRewind(request, bufferThreshold, bufferLimit); }