Aspnet 服务器渲染调试
Aspnet server rendering debugging
我在 aspnet core 上有一个 react-redux 应用程序 运行,服务器端渲染使用 aspnet 预渲染。
假设我犯了一个编程错误,在子组件中由于一个愚蠢的拼写错误我试图访问一个未定义的属性。
import {Child} from './child'
export class Parent extends React.Component {
render () {
const someProp = {
something: "something"
};
return <Child someProp={someProp} />;
}
}
export class Child extends React.Component {
render() {
return <div>this.props.someprop.something</div>;
//typo: should be someProp instead of someprop
}
如果没有服务器渲染,我会得到类似这样的错误:无法访问行 x:yy 处未定义的内容
但是通过服务器渲染我得到一个:
An unhandled exception occurred while processing the request.
Exception: Call to Node module failed with error: Prerendering timed out after 30000ms because the boot function in 'ClientApp/src/boot-server' returned a promise that did not resolve or reject. Make sure that your boot function always resolves or rejects its promise. You can change the timeout value using the 'asp-prerender-timeout' tag helper.
当您得不到任何关于出错地方的反馈时,这会使调试变得非常困难。
如果出现故障,有人知道如何设置拒绝吗?或者甚至可以调试服务器端呈现的代码?
这是我的引导服务器文件,如果您需要更多文件,请告诉我。
import * as React from 'react';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import configureStore from './store/configureStore';
import {getFormById} from './actions/getFormActions';
import {updateUserLocale} from './actions/userLocaleActions';
import FormResponder from './components/mainComponents/formResponder';
export default function renderApp (params) {
return new Promise((resolve, reject) => {
const store = configureStore();
store.dispatch(getFormById(params.data.id, params.data.config, params.data.authenticationToken));
store.dispatch(updateUserLocale(params.data.userLocale));
const app = (
<Provider store={ store }>
<FormResponder />
</Provider>
);
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
renderToString(app);
// Once the tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config
}
});
}, reject); // Also propagate any errors back into the host application
});
}
我做了一些研究,得出的结论是目前无法调试初始服务器呈现的代码。
我所做的是实现逻辑,以便我可以禁用服务器呈现。
这是它的样子:
public async Task<IActionResult> Index(string id, string userLocale = "en", bool server = true)
{
Guid positionId;
if (!Guid.TryParse(id, out positionId))
{
throw new Exception("Invalid position id");
}
var token = await _apiClient.GetToken();
var formData = new ApplicationFormViewModel()
{
Id = positionId,
UserLocale = userLocale,
AuthenticationToken = token.AccessToken,
Server = server
};
return View(formData);
}
view.cshtml:
@{if (@Model.Server) {
<div
class="container"
id="react-app"
asp-prerender-module="ClientApp/src/boot-server"
asp-prerender-data="new {
Id = @Model.Id,
UserLocale = @Model.UserLocale,
AuthenticationToken = @Model.AuthenticationToken,
Config = new {
ApplicationPostUrl = @Url.Action("SaveApplication"),
AttachmentPostUrl = @Url.Action("UploadAttachment"),
FormGetUrl = @Url.Action("GetForm")
}
}"
asp-prerender-webpack-config="webpack.config.js" >
Loading...
</div>
}
else {
<script>
var id= '@Model.Id';
var config= {
applicationPostUrl: '@Url.Action("SaveApplication")',
attachmentPostUrl: '@Url.Action("UploadAttachment")',
formGetUrl: '@Url.Action("GetForm")'
};
var userLocale='@Model.UserLocale';
var authenticationToken='@Model.AuthenticationToken';
var server = false;
</script>
<div class="container" id="react-app">loading</div>
}
}
@section scripts {
<script src="~/dist/main.js" asp-append-version="true"></script>
}
启动-server.jsx:
export default function renderApp (params) {
return new Promise((resolve, reject) => {
const store = configureStore();
store.dispatch(getFormById(params.data.id, params.data.config, params.data.authenticationToken));
store.dispatch(updateUserLocale(params.data.userLocale));
const app = (
<Provider store={ store }>
<FormResponder />
</Provider>
);
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
renderToString(app);
// Once the tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config,
server: true
}
});
}, reject); // Also propagate any errors back into the host application
});
}
启动-client.jsx:
// Grab the state from a global injected into server-generated HTML
const {id, initialReduxState, authenticationToken, config, server, userLocale } = window;
if (server) {
// Get the application-wide store instance, prepopulating with state from the server where available.
const store = configureStore(initialReduxState);
// This code starts up the React app when it runs in a browser.
ReactDOM.render(
<Provider store={ store }>
<FormResponder authenticationToken={authenticationToken} config={config} />
</Provider>,
document.getElementById('react-app')
);
}
else {
const store = configureStore();
store.dispatch(getFormById(id, config, authenticationToken));
store.dispatch(updateUserLocale(userLocale));
render(
<Provider store ={store}>
<FormResponder authenticationToken={authenticationToken} config={config} />
</Provider>,
document.getElementById('react-app')
); // Take our FormBuilder component and attach it with DOM element "app"
}
所以现在我可以通过在 url 末尾添加一个 ?server=false 来简单地关闭服务器渲染,然后开始调试 :)
找到适合我的解决方案:
我在最终的 renderToString 上插入了一个 try/catch。
在 catch 中,我发送了一个有错误的调度。
更新启动-server.jsx
params.domainTasks.then(() => {
let html;
try {
html = renderToString(app);
}
catch (err) {
store.dispatch(loadFormFailed( {message: err.toString() } ));
}
resolve({
html: html,
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config,
disableReactServerRendring: false
}
});
}, reject);
// Also propagate any errors back into the host application
});
我在使用 Visual Studio 2017 时也有类似的经历。我最终意识到原始错误的诊断信息实际上在输出 window 中。
我在 aspnet core 上有一个 react-redux 应用程序 运行,服务器端渲染使用 aspnet 预渲染。
假设我犯了一个编程错误,在子组件中由于一个愚蠢的拼写错误我试图访问一个未定义的属性。
import {Child} from './child'
export class Parent extends React.Component {
render () {
const someProp = {
something: "something"
};
return <Child someProp={someProp} />;
}
}
export class Child extends React.Component {
render() {
return <div>this.props.someprop.something</div>;
//typo: should be someProp instead of someprop
}
如果没有服务器渲染,我会得到类似这样的错误:无法访问行 x:yy 处未定义的内容 但是通过服务器渲染我得到一个:
An unhandled exception occurred while processing the request.
Exception: Call to Node module failed with error: Prerendering timed out after 30000ms because the boot function in 'ClientApp/src/boot-server' returned a promise that did not resolve or reject. Make sure that your boot function always resolves or rejects its promise. You can change the timeout value using the 'asp-prerender-timeout' tag helper.
当您得不到任何关于出错地方的反馈时,这会使调试变得非常困难。 如果出现故障,有人知道如何设置拒绝吗?或者甚至可以调试服务器端呈现的代码?
这是我的引导服务器文件,如果您需要更多文件,请告诉我。
import * as React from 'react';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import configureStore from './store/configureStore';
import {getFormById} from './actions/getFormActions';
import {updateUserLocale} from './actions/userLocaleActions';
import FormResponder from './components/mainComponents/formResponder';
export default function renderApp (params) {
return new Promise((resolve, reject) => {
const store = configureStore();
store.dispatch(getFormById(params.data.id, params.data.config, params.data.authenticationToken));
store.dispatch(updateUserLocale(params.data.userLocale));
const app = (
<Provider store={ store }>
<FormResponder />
</Provider>
);
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
renderToString(app);
// Once the tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config
}
});
}, reject); // Also propagate any errors back into the host application
});
}
我做了一些研究,得出的结论是目前无法调试初始服务器呈现的代码。
我所做的是实现逻辑,以便我可以禁用服务器呈现。
这是它的样子:
public async Task<IActionResult> Index(string id, string userLocale = "en", bool server = true)
{
Guid positionId;
if (!Guid.TryParse(id, out positionId))
{
throw new Exception("Invalid position id");
}
var token = await _apiClient.GetToken();
var formData = new ApplicationFormViewModel()
{
Id = positionId,
UserLocale = userLocale,
AuthenticationToken = token.AccessToken,
Server = server
};
return View(formData);
}
view.cshtml:
@{if (@Model.Server) {
<div
class="container"
id="react-app"
asp-prerender-module="ClientApp/src/boot-server"
asp-prerender-data="new {
Id = @Model.Id,
UserLocale = @Model.UserLocale,
AuthenticationToken = @Model.AuthenticationToken,
Config = new {
ApplicationPostUrl = @Url.Action("SaveApplication"),
AttachmentPostUrl = @Url.Action("UploadAttachment"),
FormGetUrl = @Url.Action("GetForm")
}
}"
asp-prerender-webpack-config="webpack.config.js" >
Loading...
</div>
}
else {
<script>
var id= '@Model.Id';
var config= {
applicationPostUrl: '@Url.Action("SaveApplication")',
attachmentPostUrl: '@Url.Action("UploadAttachment")',
formGetUrl: '@Url.Action("GetForm")'
};
var userLocale='@Model.UserLocale';
var authenticationToken='@Model.AuthenticationToken';
var server = false;
</script>
<div class="container" id="react-app">loading</div>
}
}
@section scripts {
<script src="~/dist/main.js" asp-append-version="true"></script>
}
启动-server.jsx:
export default function renderApp (params) {
return new Promise((resolve, reject) => {
const store = configureStore();
store.dispatch(getFormById(params.data.id, params.data.config, params.data.authenticationToken));
store.dispatch(updateUserLocale(params.data.userLocale));
const app = (
<Provider store={ store }>
<FormResponder />
</Provider>
);
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
renderToString(app);
// Once the tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config,
server: true
}
});
}, reject); // Also propagate any errors back into the host application
});
}
启动-client.jsx:
// Grab the state from a global injected into server-generated HTML
const {id, initialReduxState, authenticationToken, config, server, userLocale } = window;
if (server) {
// Get the application-wide store instance, prepopulating with state from the server where available.
const store = configureStore(initialReduxState);
// This code starts up the React app when it runs in a browser.
ReactDOM.render(
<Provider store={ store }>
<FormResponder authenticationToken={authenticationToken} config={config} />
</Provider>,
document.getElementById('react-app')
);
}
else {
const store = configureStore();
store.dispatch(getFormById(id, config, authenticationToken));
store.dispatch(updateUserLocale(userLocale));
render(
<Provider store ={store}>
<FormResponder authenticationToken={authenticationToken} config={config} />
</Provider>,
document.getElementById('react-app')
); // Take our FormBuilder component and attach it with DOM element "app"
}
所以现在我可以通过在 url 末尾添加一个 ?server=false 来简单地关闭服务器渲染,然后开始调试 :)
找到适合我的解决方案: 我在最终的 renderToString 上插入了一个 try/catch。 在 catch 中,我发送了一个有错误的调度。
更新启动-server.jsx
params.domainTasks.then(() => {
let html;
try {
html = renderToString(app);
}
catch (err) {
store.dispatch(loadFormFailed( {message: err.toString() } ));
}
resolve({
html: html,
globals: {
initialReduxState: store.getState(),
authenticationToken: params.data.authenticationToken,
config: params.data.config,
disableReactServerRendring: false
}
});
}, reject);
// Also propagate any errors back into the host application
});
我在使用 Visual Studio 2017 时也有类似的经历。我最终意识到原始错误的诊断信息实际上在输出 window 中。