一:背景

1. 讲故事

前几天群里有一位朋友聊到,为什么我在 Action 中执行一句 Response.Write 之后,后续的 View 就不呈现了,如果脑子中没有画面,那就上测试代码:

public class HomeController : Controller{public IActionResult Index(){Response.WriteAsync("hello world!");return View();}}

结果还是挺有意思的,大家都知道,默认情况下会渲染 /Home/Index 对应的 view 页面,但这里被 Response.WriteAsync 插了一杠子,气的 view 都渲染不出来了,那接下来就来找一找 view 为啥这么生气?

二:寻找真相

1. 从 Logger 入手

相信很多人都在用 aspnetcore 中的 logger 记录日志,为什么要首选这个 logger 呢?因为它在 web框架 中是一等公民的存在,毕竟底层源码各处都嵌入着这玩意哈,随便找点代码:


internal abstract class ActionMethodExecutor
{private Task ResultNext<TFilter, TFilterAsync>(ref ResourceInvoker.State next, ref ResourceInvoker.Scope scope, [Nullable(2)] ref object state, ref bool isCompleted) where TFilter : class, IResultFilter where TFilterAsync : class, IAsyncResultFilter{ResourceInvoker.ResultExecutingContextSealed resultExecutingContext3 = this._resultExecutingContext;this._diagnosticListener.BeforeOnResultExecuting(resultExecutingContext3, tfilter);this._logger.BeforeExecutingMethodOnFilter(filterType, "OnResultExecuting", tfilter);tfilter.OnResultExecuting(resultExecutingContext3);this._diagnosticListener.AfterOnResultExecuting(resultExecutingContext3, tfilter);this._logger.AfterExecutingMethodOnFilter(filterType, "OnResultExecuting", tfilter);if (this._resultExecutingContext.Cancel){this._logger.ResultFilterShortCircuited(tfilter);this._resultExecutedContext = new ResourceInvoker.ResultExecutedContextSealed(resultExecutingContext3, this._filters, resultExecutingContext3.Result, this._instance){Canceled = true};goto IL_39E;}}
}

而且大家想想,这种写法特别奇葩,我想底层框架中的 logger 定会有所反馈,接下来在启动程序的时候采用  WebApplication1 的模式启动,如下图:

启动后,在控制台上可以看到一堆报错信息:


info: Microsoft.Hosting.Lifetime[0]Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]Content root path: E:\net5\WebApplication1\WebApplication1
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Headers are read-only, response has already started.at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)at Microsoft.AspNetCore.Http.DefaultHttpResponse.set_ContentType(String value)at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable`1 statusCode)at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()

异常信息非常明显:Headers are read-only, response has already started,大概就是说,header是只读的,response已是启动状态了,从调用堆栈的 ViewExecutor.ExecuteAsync 处可看出,代码准备渲染 view,在 set_ContentType 处遭遇异常,结束了后续渲染流程。

接下来一起看下,为什么会触发这个异常???

三:调试源码寻找异常的原因

1. dnspy 调试

除了从异常堆栈中找到最早的异常代码处,这里还说一个小技巧,使用 ndspy 的 异常断点功能,在异常设置面板 定位  InvalidOperationException 异常即可。

接下来就可以让程序跑起来,当异常抛出时会自动断下来。

仔细看一下图中的文字标注,还是很好理解的,接下来继续追一下:response.ContentType = contentType2; 内部都做了什么。

public override string ContentType{get{return this.Headers[HeaderNames.ContentType];}set{if (string.IsNullOrEmpty(value)){this.HttpResponseFeature.Headers.Remove(HeaderNames.ContentType);return;}this.HttpResponseFeature.Headers[HeaderNames.ContentType] = value;}}

可以看到 内部是给 this.HttpResponseFeature.Headers 赋值的,继续往下追:

从图中可以看到,最后的 HttpHeader._isReadOnly =true 导致异常的发生,罪魁祸首哈,接下来研究下这句 HttpHeader._isReadOnly=true 是何时被赋值的。

2.  _isReadOnly=true 何时发生

这个问题就简单多了,必定是 Response.WriteAsync("hello world!"); 造成了 _isReadOnly=true ,在 HttpHeader 下有一个 SetReadOnly 方法用于对 _isReadOnly 字段的封装,代码如下:


internal abstract class HttpHeaders
{public void SetReadOnly(){this._isReadOnly = true;}
}        

接下来在该方法处下一个断点,继续调试,如下图:

从图中可看到,原来 Response.WriteAsync("hello world!") 是可以封锁 HttpHeaders的,后续任何再对 HttpHeader 的操作都是无效的。。。

其实大家也可以想一想,不同的response,肯定会有不同的 header,要想叠加的话这辈子都不可能的,只能让后面的报错,如下:


1. response:HTTP/1.1 200 OK
Date: Mon, 19 Oct 2020 14:37:54 GMT
Server: Kestrel
Transfer-Encoding: chunkedc
hello world!2. view:HTTP/1.1 200 OK
Date: Mon, 19 Oct 2020 14:39:01 GMT
Content-Type: text/html; charset=utf-8
Server: Kestrel
Content-Length: 2239

四:总结

这篇就是对群聊天过程中抛出问题的个人探究,一家之言,不过挺有意思,大家也可以多用用调试工具寻找问题,证明问题,纸上得来终觉浅,绝知此事要躬行,好了,希望本篇对您有帮助!

为啥 Response.Write 后,View就不渲染了?相关推荐

  1. SAP MM 明明有需求,为啥MRP RUN后没有PR单据产生?

    SAP MM 明明有需求,为啥MRP RUN后没有PR单据产生? 用户报了一个问题说,对于物料号42011222的采购单 4500000156建好了,为啥PR没有自动生成 . 我们检查了物料的MRP ...

  2. 【Agni-s Philosophy】使用的图形技术解说(后篇)Volume渲染和粒子处理

    在2012年11月举办的[SQUARE ENIX 开放会议 2012]的第2天,进行了新世代游戏引擎[Luminous Studio]制作的实时技术演示作品[Agni's Philosophy]中使用 ...

  3. 【千方百计】更改绑定的数据对象数值后,DOM重新渲染的4种方法

    本篇博客是博主记录在项目开发中遇到的Vue前端界面UI更新问题,界面更新就是对界面元素的更新.下述4中方法均是Vue框架本身提供的更新UI界面的API,按照接口对UI刷新操作后影响的程度进行升级描述. ...

  4. Android-一只手指滑动View,另一只手指按Home键,重新进入后View状态无法更新的问题...

    上午测试报了一个bug:说是一只手指拖动虚拟摇杆上的View滑块不松,另一只手指点击Home键将App压后台,重新进入的时候,View滑块卡死了. 刚开始看到这个问题感觉很奇怪,因为正常情况下,手指离 ...

  5. 前端后分离深入分析 ——浏览器渲染和服务器渲染区别

    1.为什么会有服务器渲染与客户端渲染? 首先理解服务器和浏览器客户端之间传递的是什么--HTML,CSS,JavaScript的文件以及数据载体json(xml)等文件,而文件都是静态,之所以动态是应 ...

  6. response.end后抛了异常_(七)异常处理

    (七)异常处理 异常 异常的体系结构 java.lang.Throwable|-----java.lang.Error:一般不便携针对性的代码进行处理|-----java.lang.Exception ...

  7. maya导入模型后贴图没渲染怎么办?

    你先试试按6,如果没显示,那么你用这个模型材质书所支持的渲染器渲染一下,如果还没有,打开好像是叫路径管理器的东西,看看里面有没有爆红,爆红说你你贴图路径没有指定过去

  8. 【android】 Android 动画cancle后 view隐藏

    如果给view添加一个anim,之后调用 anim.cancel(); view.setVisibility(View.GONE); 就是view 隐藏不了???? 咋会隐藏不了,其实在View.GO ...

  9. uniapp修改data数据后页面未更新渲染

    如下代码块,根据打印的结果,mediaList数据已经被修改,但是页面没有渲染出来 _this.mediaList[index].currentTime = hxt.formatSeconds(_th ...

最新文章

  1. 数据中心基础运维人员的职业规划
  2. java 快排_百度在年前会在打击一轮快排!
  3. SQL Server2005 使用FOR XML选项进行字符串的串联聚合
  4. [转]Git分支管理策略
  5. Oculus cv1 input
  6. php和python-浅析PHP与Python进行数据交互
  7. 错误解决办法:zipimport.ZipImportError: can't decompress data; zlib not available
  8. day-60Django
  9. 推荐一款专业串烧歌曲的音乐合并软件
  10. Windows密码查看器实现原理
  11. cad打印去掉边框_CAD打印的时候如何去掉打印线框?
  12. Topaz Adjust AI Mac
  13. mac 运行android模拟器速度慢,Mac下顺畅的安卓模拟器:网易MuMu
  14. 程序流程图、电商项目开发流程图模板
  15. 吴伯凡-认知方法论-效率高并不一定是好事
  16. lifecycle-aware components(生命周期感知组件用法和原理)
  17. 格密码LLL算法:如何解决最短向量SVP问题(3)(完结篇)
  18. 用友NC65“该产品的用户数已达到产品授权数”的伪故障
  19. aurora_8b10b通信
  20. libjpeg的安装与使用

热门文章

  1. windows 安装yaml支持和pytest支持等
  2. 淘宝一淘网收录部分垂直B2C网站信息
  3. java 中的 io 系统总结
  4. 大学生计算机课程考试试题,大学生计算机基础课程考试系统研究与实现
  5. c#写字板实现加粗功能_Windows 7中写字板和绘画中的新功能
  6. 火狐和chrome_Firefox,Chrome和Edge都将支持WebAuthn的硬件两因素身份验证
  7. Ubuntu系统备份工具大全(官方整理推荐)
  8. Struts2 OGNL
  9. 使用T-SQL找出执行时间过长的作业
  10. windows下编译firefox