上下文:启动一个F#应用程序,该应用程序使用服务器侧blazor/razor组件,但不使用razor来定义组件。相反,我使用FsBoleros DSL。
一切都如预期的那样工作,但.cshtml
文件(_Host.cshtml
(上仍然有我想用(F#(代码替换的内容,原因有几个:
-
我所有的代码都将用F#编写,但如果我需要更改内容/向主机剃刀文件添加代码,我需要混合使用C#。虽然这是可行的,但它不是最佳的,我宁愿只有F#代码,没有特殊的剃刀文件。
-
F#项目取决于文件顺序。剃刀文件在这方面表现得有点奇怪(我不能像使用IDE工具的项目中的其他文件一样重新排序(。
所以目标是去掉_Host.cshtml
文件,并用代码替换它。
我已经花了两天的时间来尝试做这件事了。关于如何用代码替换剃刀页面,或者如何从非剃刀代码中调用标记助手,似乎几乎没有内容。如果能给我一点关于如何处理这件事的指导,我将不胜感激,否则我会在这件事上浪费更多的时间。
我目前对我必须做的事情的理解是:
呈现包含的html响应
- 服务器端blazor框架
- 用特殊注释注释的预呈现的根组件
这是正确的吗?
如果是:
除了预先呈现的带注释的组件之外,生成所需的html页面是微不足道的。调用标记助手是实现这一目标的正确方法吗(因为我对此有一些问题(?
其他组件呈现类(和接口(是内部的,所以我怀疑不应该使用它们。我还假设我必须使用标记助手才能正确地注释html。
注意:实际上不需要用完全相同的代码来替换它(例如,一个剃刀页面(,而是以可维护的方式执行引导blazor所需的操作(因此没有魔术串管道(。
(这是要替换的_Host.cshtml
文件(
@page "/"
@namespace X.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Bolero Application</title>
<base href="~/">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<h1>Blazor Host</h1>
<component type="typeof(App)" render-mode="ServerPrerendered"/>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
EDIT:在打开正确的名称空间后,使用IHtmlHelper
似乎也可以做到这一点。
task {
let htmlHelper = container.GetService<IHtmlHelper>()
(htmlHelper :?> IViewContextAware).Contextualize(ViewContext(HttpContext = httpContext))
// RenderComponentAsync is an extension from "Microsoft.AspNetCore.Mvc.Rendering"
let! componentHtmlContent = htmlHelper.RenderComponentAsync<App>(RenderMode.ServerPrerendered)
let componentHtml: string =
using (new StringWriter()) (fun writer ->
componentHtmlContent.WriteTo (writer, HtmlEncoder.Default)
writer.Flush()
writer.ToString()
)
}
老答案如下:
我发现的解决方案是有效的,但涉及到对ASP未公开的内部类型调用方法。NET核心。
要调用IComponentRenderer的模块。通过反射进行RenderComponentAsync。
[<RequireQualifiedAccess>]
module ComponentRenderer =
open System
open System.Reflection
open System.Threading.Tasks
open Microsoft.AspNetCore.Html
open Microsoft.AspNetCore.Mvc.Rendering
let private ``reflected IComponentRenderer``: Type =
let assemblyName = "Microsoft.AspNetCore.Mvc.ViewFeatures"
let assembly = Assembly.Load assemblyName
let componentRendererInterface = assembly.GetType $"{assemblyName}.IComponentRenderer"
componentRendererInterface
let private getRef (serviceContainer: IServiceProvider) : obj =
serviceContainer.GetService ``reflected IComponentRenderer``
let private getMethodRef () : MethodInfo =
let flags = BindingFlags.Public ||| BindingFlags.Instance
let method = ``reflected IComponentRenderer``.GetMethod ("RenderComponentAsync", flags)
method
let callRenderComponentAsync
(serviceContainer: IServiceProvider,
viewContext: ViewContext,
componentType: Type,
renderMode: RenderMode,
parameters: obj) : Task<IHtmlContent> =
let renderer = getRef serviceContainer
let method = getMethodRef ()
let result = method.Invoke(renderer, [| viewContext; componentType; renderMode; parameters |])
result :?> Task<IHtmlContent>
基本上,所需要的只是一个HTML响应,其中包含:
- 服务器端blazor框架
- 用特殊注释注释的预呈现的根组件
有多种方法可以获得所需的结果,下面是一个简单的处理程序示例。
module HttpHandlers =
let htmlTemplate (comp: string) =
$"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Bolero Application</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
{comp}
<script src="_framework/blazor.server.js"></script>
</body>
</html>
"""
let index (container: IServiceProvider) (httpContext: HttpContext) : Task =
task {
let! componentHtmlContent =
ComponentRenderer.callRenderComponentAsync (
container,
ViewContext(HttpContext = httpContext),
typeof<App>,
RenderMode.ServerPrerendered,
null
)
let componentHtml: string =
using (new StringWriter()) (fun writer ->
componentHtmlContent.WriteTo (writer, HtmlEncoder.Default)
writer.Flush()
writer.ToString()
)
let! _ =
(htmlTemplate componentHtml)
|> Encoding.UTF8.GetBytes
|> ReadOnlyMemory
|> httpContext.Response.BodyWriter.WriteAsync
return ()
} :> _
生成的html响应现在应该是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<!--Blazor:{"sequence":0,"type":"server","prerenderId":"de4cefeb845c4241810b39a9fa6fb09d","descriptor":"CfDJ8B1PPZs/SbNIpyDpD4CgsZTZ9MSJjiAAdULVygxQGjm2QdNlb19sKvxXVu002B0ZZhu002Br43icwKyOdOw0RCK6auj2fziShHMZNiE/kNp0XnqJywdEpAAGuNROLgewx4NSSwKJ5lUUWqu002BiuwKOnKNwQEJA1BAQ1B0IJbE6gKqHkKPMK3vYVjnB/jgpX01f2DS7djVlHu002Bc/D8hr2jjuqt08527OrAPky7Fm71HejVDjEwZApZUj853dq3sDpmyNO2uWJaTRufSeBX1UISwofgBwDobZ8RBSVTfzMP8HPJu002BKBJxtu002BNXueXpxcXXQwva9n5tqWKyFEahW4lOQFLrr3/Gvh9mRY1EExZapEiO/b5qHc9CtwgqDQN8U7fwtH2il8uPBs3Hsdg=="}-->
<div>
<p>Hello World and welcome to my app!</p>
</div>
<!--Blazor:{"prerenderId":"de4cefeb845c4241810b39a9fa6fb09d"}-->
<script src="_framework/blazor.server.js"></script>
</body>
</html>
查看生成的代码文件_Host.cshtml.g.cs
并将其转换为F#
我不做F#,所以不能评论这会有多困难,但审查生成的代码至少应该回答你关于如何调用taghelpers等的问题