在获得创意后,我找到了解决问题的方法,而不是完全按照我的要求做的解决方案。在我发现 JavaScript <script> 块仍然会被浏览器处理,即使它们在关闭 </html> 标记之后被插入到 DOM 中,我意识到我可以利用即时函数来添加我希望它们出现的元素身体。我的解决方案如下:
- MVC 控制器将完整的 HTML 页面写入响应并
将其刷新回客户端
- 随着后续步骤的处理,控制器写入
响应的脚本块并刷新它们
- 这些脚本块是即时函数,用于操作 DOM 组件以在实际终止请求之前添加我想要显示的步骤
示例
控制器 .cs 文件:
[Route("example")]
public class ExampleController : Controller
{
private readonly ILogger<ExampleController> _logger;
private readonly IExampleControllerBL _controllerBl;
private const string htmlPage = @"
<!DOCTYPE HTML>
<html>
<head>
<title>Learning Management - Course Auto-Enrollment</title>
<style>
body {
font-size: 1.15em;
color: #333;
font-family: Arial, Helvetica;
height:100%;
}
.stepRow {
display: flex;
width: 100% ;
}
.stepCell, {
display: flex;
height: 2rem;
align-items: center;
}
#output {
min-height: 350px;
padding-bottom: 15px 0;
}
#footer {
background-color: #ccc;
padding: 20px;
margin: 0;
}
</style>
<script>
function addStep(stepText)
{
var output = document.querySelector('#output');
var stepRow = document.createElement('div');
var stepCell = document.createElement('div');
stepRow.className = 'stepRow';
stepCell.className = 'stepCell';
stepRow.appendChild(stepCell);
output.appendChild(stepRow);
stepCell.innerHTML = stepText;
}
</script>
</head>
<body>
<h1>Flushed Response Example</h1>
<p>Example of flushing output back to the client and then redirecting when complete.</p>
<h3>Progress:</h3>
<div id=""output""></div>
<div id=""footer"">Footer here</div>
</body>
</html>
";
private const string stepTemplate = @"
<script>
(function(){{
addStep('• {0}');
}}());
</script>
";
private const string redirectTemplate = @"
<script>
(function(){{
window.location.replace('{0}');
}}());
</script>
";
public ExampleController(ILogger<ExampleController> logger, IExampleControllerBL controllerBl)
{
_logger = logger;
_controllerBl = controllerBl;
}
[HttpGet]
[Route("steps"), HttpGet]
public async Task SteppedResponseExample(CancellationToken cancellationToken)
{
//Write full HTML page back to response
Response.Clear();
await Response.WriteAsync(htmlPage, cancellationToken);
//Execute business logic and pass step change handler
await _controllerBl.ExecuteMultiStepProcess(async (step) =>
{
//Write step script block back to response
await Response.WriteAsync(string.Format(stepTemplate, step), cancellationToken);
});
//Write redirect script block to response
await Response.WriteAsync(string.Format(redirectTemplate, "https://stackoverflow.com/questions/57330559/asp-net-core-controlling-httpresponse-flush-output-location"), cancellationToken);
}
}
业务逻辑.cs文件:
public class ExampleControllerBL : IExampleControllerBL
{
public async Task ExecuteMultiStepProcess(Func<string, Task> OnStartStep)
{
await OnStartStep("Performing step 1...");
await Task.Delay(2500);
await OnStartStep("Performing step 2...");
await Task.Delay(2500);
await OnStartStep("Performing step 3...");
await Task.Delay(2500);
await OnStartStep("Performing step 4...");
await Task.Delay(2500);
await OnStartStep("Performing step 5...");
await Task.Delay(2500);
await OnStartStep("Performing step 6...");
await Task.Delay(2500);
}
}
一旦您开始刷新响应,尝试返回 IAction 结果对象是没有意义的,因为 MVC 中间件管道会抛出错误,因为状态码已经通过刷新设置等。这就是我的端点不返回任何内容的原因。
显然需要考虑错误处理,因为它还必须通过刷新来处理,除非您只是想让最终用户对它停止的原因感到困惑。但是,这是让最终用户了解涉及重定向和处理的过程的好方法。希望这对其他人有用。
编辑:我确实发现在这个用例中实际上不需要“刷新”响应正文。