为啥 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: 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

四:总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3. 无重复字符的最长子串(滑动窗口详解版)

一:题目 二&#xff1a;上码 class Solution {/**分析&#xff1a;1.滑动窗口问题2.何时更改窗口的起点位置当出现重复的时候 需要从重复的字符的下一个字符开始计算*/public int lengthOfLongestSubstring(String s) {if (s.length() < 1) {return s.length();}int slowIn…

121. 买卖股票的最佳时机

一:题目 二:上码 class Solution {// public int maxProfit(int[] prices) {// int max 0;// for (int i 0; i < prices.length; i) {// //求出i后面的最大值// int temp 0;// for (int j i 1; j < prices.length; j) {// …

IdentityServer4系列 | 快速搭建简易项目

一 、前言从上一篇关于 常见术语说明中&#xff0c;主要是对「IdentityServer4」的说明&#xff0c;以及其中涉及常见的术语的表述说明&#xff0c;包括对身份认证服务器、用户、客户端、资源以及各个令牌等进行对比区别说明。而在这一篇中&#xff0c;我们将尝试通过简单的方式…

136. 只出现一次的数字(hot100)

一:题目 二:上码 class Solution {public int singleNumber(int[] nums) {int ans -100;Arrays.sort(nums);for (int i 0; i < nums.length-1; i2) {if (nums[i] ! nums [i1]) {ans nums[i];break;}}return ans -100 ? nums[nums.length-1] : ans;} }

高并发项目Java是标配?.NET Core要将它拉下“神坛”!

电商的秒杀和抢购&#xff0c;对我们来说&#xff0c;都不是一个陌生的东西。然而&#xff0c;从技术的角度来说&#xff0c;这对于Web系统是一个巨大的考验。当一个Web系统&#xff0c;在一秒钟内收到数以万计甚至更多请求时&#xff0c;系统的优化和稳定至关重要。缓存技术是…

141. 环形链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public boolean hasCycle(ListNode head) {ListNode fa…

程序员修神之路--分布式系统使用网关到底是好还是坏?

“灵魂拷问分布式系统需要统一的网关吗&#xff1f;网关会带来哪些优势&#xff1f;引入网关会带来灾难吗&#xff1f;分布式系统的设计大体上分为中心化和非中心化&#xff0c;像现在流行的微服务模式&#xff0c;本质上是把各种业务拆分为独立的进程来实现业务的扩展性。伴随…

160. 相交链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListN…

.NET Core开源任务调度平台ScheduleMaster上新了

ScheduleMaster上一次比较大的更新还是在6月份&#xff0c;转眼已经快过去4个月了&#xff0c;这段时间比较忙&#xff0c;中间只更新过一次修复了几个小bug。要总结这次更新的话&#xff0c;必须要用“千呼万唤始出来”了&#xff0c;因为这次不仅经历的时间比较久&#xff0c…

234. 回文链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ …

T-SQL | 逻辑查询处理内幕学习

【T-SQL】| 作者 / Edison Zhou这是EdisonTalk的第296篇学习分享T-SQL是ANSI和ISO SQL标准的MS SQL扩展&#xff0c;其正式名称为Transact-SQL&#xff0c;但一般程序员都称其为T-SQL。本文是我学习《T-SQL查询》一书的读书笔记&#xff0c;为你讲解逻辑查询的内幕。1逻辑查询处…

461. 汉明距离(详解)

一:题目 二:上码 class Solution {/**在信息论中&#xff0c;两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数换句话说就是将字符串转换成另一个字符串需要替换的字符的个数*/public int hammingDistance(int x, int y) {int count 0;if (x y) return 0;…

在 Azure 上给我的博客配置负载均衡

点击上方蓝字关注“汪宇杰博客”导语前阵有美国读者嘲讽我的博客在美国地区页面加载速度太慢&#xff0c;还好意思写性能优化的文章。为了让美国朋友们闭嘴&#xff0c;并不给中国人丢脸&#xff0c;我使用了钞能力&#xff0c;在 Azure 国际版上给我的博客部署了一个美国地区的…

543. 二叉树的直径(详解)

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

C#实现乞丐版IOC容器

一、前言netcore中的容器非常好用&#xff0c;今天我们自己来简单实现一个。实现容器的核心接口有两个&#xff1a;IServiceCollection、IServiceProvider。其中IServiceCollection定义容器集合&#xff0c;IServiceProvider提供容器中对象的访问。话不多说&#xff0c;直接编码…

leetcode hot100.2. 两数相加(详解)

一:题目 二:上码 // /** // * Definition for singly-linked list. // * public class ListNode { // * int val; // * ListNode next; // * ListNode() {} // * ListNode(int val) { this.val val; } // * ListNode(int val, ListNode next) { t…

COSCon‘20 参会指南 | 你想知道的都在这里(文末有福利)

中国开源年会 COSCon20&#xff0c;本周末你准备好了吗业界最具影响力的开源年度盛会2020中国开源年会 (COSCon20) 将于 10月24-25日由开源社举办。本次大会较往年出现了许多新变化&#xff0c;您可能有许多问题&#xff0c;为了让大家尽快参与其中&#xff0c;小编在这里为大家…

leetcode647. 回文子串(动态规划)

一:题目 二:上码 class Solution {/**思路:1.确定dp数组dp[i][j]:表示的是区间范围内[i,j]的字符串是否是回文串 2.确定dp数组递推公式1>:如果s[i] ! s[j] 那就是false2>:如果s[i] s[j] 如果 i - j < 1 那么的话 dp[i][j] 就为true如果 i - j > 1 的话 那就要看…

搬砖的八种境界

这一点文字&#xff0c;纯属有感而发。世上的道路千万条&#xff0c;搬砖的队伍更是前仆后继&#xff0c;延绵不绝。我总结有八种境界&#xff0c;请各位看官对号入座&#xff0c;不要客气。不知道怎么搬砖。职业早期&#xff0c;无可厚非。但如果长期处于这个阶段&#xff0c;…

芯片项目谁支持谁负责 重大损失将予以通报

随着全国掀起半导体热&#xff0c;全国各地都在大力投资&#xff0c;其中不乏浑水摸鱼的存在&#xff0c;在江苏、四川、湖北、贵州已经有多个半导体大项目先后停摆。在南京&#xff0c;号称投资30亿美元的德科码已经烂尾。在淮安&#xff0c;规划总投资450亿元的德淮半导体烂尾…