为啥 Response.Write 后,View就不渲染了?
一:背景
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: chunked
c
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
四: 总结
这篇就是对群聊天过程中抛出问题的个人探究,一家之言,不过挺有意思,大家也可以多用用调试工具寻找问题,证明问题,纸上得来终觉浅,绝知此事要躬行,好了,希望本篇对您有帮助!
更多高质量干货:参见我的 GitHub: dotnetfly
为啥 Response.Write 后,View就不渲染了?的更多相关文章
- vue修改对象的属性值后页面不重新渲染
原文地址:vue修改对象的属性值后页面不重新渲染 最近项目在使用vue,遇到几次修改了对象的属性后,页面并不重新渲染,场景如下: HTML页面如下: [html] view plain copy &l ...
- 解决springmvc中文件下载功能中使用javax.servlet.ServletOutputStream out = response.getOutputStream();后运行出异常但结果正确的问题
问题描述: 在springmvc中实现文件下载功能一般都会使用javax.servlet.ServletOutputStream out = response.getOutputStream();封装 ...
- SAP MM 明明有需求,为啥MRP RUN后没有PR单据产生?
SAP MM 明明有需求,为啥MRP RUN后没有PR单据产生? 用户报了一个问题说,对于物料号42011222的采购单 4500000156建好了,为啥PR没有自动生成 . 我们检查了物料的MRP ...
- 关于QT Graphics View开启OpenGL渲染后复选框、微调框等无法正常显示的问题
之前学习QT Graphics View框架,除了基本的图元外,还可以通过QGraphicsProxyWidget类添加QT的基本Widget(如按钮.复选框.单选框等),常使用的场景类接口如下: Q ...
- Android 自定义可拖拽View,界面渲染刷新后不会自动回到起始位置
以自定义ImageView为例: /** * 可拖拽ImageView * Created by admin on 2017/2/21. */ public class FloatingImageVi ...
- 手势识别官方教程(4)在挑划或拖动手势后view的滚动用ScrollView和 HorizontalScrollView,自定义用Scroller或OverScroller
简单滚动用ScrollView和 HorizontalScrollView就够.自定义view时可能要自定义滚动效果,可以使用 Scroller或 OverScroller Animating a S ...
- iOS 动画结束后 view的位置 待完善
默认的动画属性,动画结束后,view会回到原始位置.但是如果设定了 CAAnimation的 removedOnCompletion 属性,那么view会保持这个位置!但是真实的接收 点击的frame ...
- 解决TableView / ScrollView上的Menu问题(1滑出View区域还可点击2导致点击menu后View不能滑动)
解决TableView / ScrollView上的Menu问题 1划出区域还可点击 重写CCMenu的触摸事件函数 TouchBegin/TouchMove/TouchCancle/TouchEnd ...
- Android-一只手指滑动View,另一只手指按Home键,重新进入后View状态无法更新的问题
上午测试报了一个bug:说是一只手指拖动虚拟摇杆上的View滑块不松,另一只手指点击Home键将App压后台,重新进入的时候,View滑块卡死了. 刚开始看到这个问题感觉很奇怪,因为正常情况下,手指离 ...
随机推荐
- [LeetCode] 207. 课程表(拓扑排序,BFS)
题目 现在你总共有 n 门课需要选,记为 0 到 n-1. 在选修某些课程之前需要一些先修课程. 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1] 给定课程总量 ...
- [剑指Offer]18-题目一:删除链表的节点 题目二:删除链表中重复节点
题目一 题目 O(1)时间复杂度删除给定链表节点. 题解 用待删除节点后一个节点的值覆盖待删除节点值,更新链接关系. 注意链表只有一个节点:删除尾结点:删除头节点的处理. 代码 class ListN ...
- java安全编码指南之:字符串和编码
目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中使用 ...
- 高可用集群之keepalived+lvs实战
keepalived简介 lvs在我之前的博客<高负载集群实战之lvs负载均衡-技术流ken>中已经进行了详细的介绍和应用,在这里就不再赘述.这篇博文将把lvs与keepalived相结合 ...
- k8s集群监控(十一)
Weave Scope 在我之前的docker监控中<Docker容器监控(十)–技术流ken>就已经提到了weave scope. Weave Scope 是 Docker 和 Ku ...
- springboot:druid 404
druid配置是在servlet中添加,所以需要servlet扫描 aplication里添加@ServletComponentScan("com")
- tf.split函数的用法(tensorflow1.13.0)
tf.split(input, num_split, dimension): dimension指输入张量的哪一个维度,如果是0就表示对第0维度进行切割:num_split就是切割的数量,如果是2就表 ...
- SSH 框架常见错误
新手学习SSH框架的时候总会遇到很多问题,一碰到404 或者500错误就不知道怎么解决. 404错误是很常见的一个错误,如果没有用框架基本上只可能是没有这个路径或者文件,但是用了框架之后404的原因就 ...
- redis过期策略以及内存淘汰机制(理论+配置)
一.redis的过期策略: redis的过期策略是:定期删除+惰性删除redis在存储数据时,可能会设置过期时间,而所谓的定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的k ...
- Java 内功修炼 之 数据结构与算法(一)
一.基本认识 1.数据结构与算法的关系? (1)数据结构(data structure): 数据结构指的是 数据与数据 之间的结构关系.比如:数组.队列.哈希.树 等结构. (2)算法: 算法指的是 ...