自定义错误 XPage:浏览器加载和执行 JS 脚本 Link 或加载错误后阻止的能力
Custom Error XPage: Ability for Browser to Load and Execute JS Script Link or Block After Being Loaded On Error
情况
我正在使用自定义错误 XPage,高度偏离 the XSnippet from Tony McGuckin. It works rather well but I would like for the browser to execute a client-side JavaScript block (or load and run a JS file from a given URL). If I navigate directly to the custom error XPage, it loads correctly, but given the nature of how it loads on redirect from a SSJS runtime error, it seems to load any attempts at loading a script block in the head tag, inside the body tag. I've attempted passing through a JS script tag in the body (shown in the code below), attempted using the xp:headTag inside xp:resources, and attempted via an xp:script tag in xp:resources。
浏览器视角
从浏览器的角度来看,在部分刷新期间调用 SSJS 的事件期间遇到 运行 时间错误后,xhr 被调用 returns 500 并将内容设置到正文中标记(屏幕截图)。
查看响应内容时,整个自定义错误 XPage 都在那里,包括 <script type="text/javascript">console.log("hello world");<script>
。这似乎不会触发或向浏览器的 JS 控制台输出任何内容。通过 JS 控制台可见的是来自 dojo 的一些垃圾,抱怨返回一个响应代码为 500 的 XHR(我的 dojoConfig 通过 xsp.client.script.dojo.djConfig 设置为 isDebug: true
在 XSP 属性中)。
问题
在加载自定义错误 XPage 期间发生错误 500 后,是否有办法让客户端 JS 脚本标记在浏览器中加载和执行?
这是我的错误页面的代码。要重现我的结果,调用一个 SSJS 操作导致 运行 时间错误(例如 Paul Withers 的 OpenLog Logger for XPages 项目中包含的 ErrorOnClick XPage)和部分刷新事件。
Error.xsp(在XSP Properties中设置为错误页面)
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/xsp/core xsdxp://localhost/xsp~core.xsd"
pageTitle="${javascript:database.getTitle() + ' | Error'}">
<style
type="text/css"><![CDATA[
body {
background-color: lightblue;
}
form {
width: 1000px !important;
margin: 0 auto !important;
background-color: white !important;
margin-top: 2rem !important;
padding: 0.5rem !important;
height: auto;
}
.xspTextLabel {
font-weight: bold !important;
}
]]></style>
<img
class="logo-simple"
src="//placehold.it/124x32" />
<xp:panel>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
style="font-weight:bold;font-size:12pt"
value="An Unexpected Error Has Occurred:">
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Error:"></xp:label>
<xp:br />
<xp:text
escape="false">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var output = (requestScope.error.toString() || null)+"<br /><br />";
if(requestScope.error instanceof com.ibm.xsp.exception.XSPExceptionInfo){
var codeSnippet = requestScope.error.getErrorText();
var control = requestScope.error.getErrorComponentId();
var cause = requestScope.error.getCause();
output += "In the control : " + control + "<br /><br />";
if(cause instanceof com.ibm.jscript.InterpretException){
var errorLine = cause.getErrorLine();
var errorColumn = cause.getErrorCol();
output += "At line " + errorLine;
output += ", column " + errorColumn + " of:<br />";
}else{
output += "In the script:<br />";
}
if( @Contains(codeSnippet,"#{javascript:") ){
var snipAr = codeSnippet.split("#{javascript:");
var tmpSnip = snipAr[1];
var nwSnip = tmpSnip.substring(0, tmpSnip.length - 1);
output += "#{javascript:<br /><pre>"+nwSnip+"</pre>}"
}else{
output += codeSnippet;
}
}
return output;
}else{
return "";
}}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Stack Trace:"></xp:label>
<xp:br />
<xp:text
escape="false"
style="font-size:10pt">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var stackTrace = "";
var trace = (requestScope.error.getStackTrace() || null);
if(trace != null){
for(var i = 0; i < trace.length; i++){
stackTrace += trace[i] + "<br/>";
}
return "<pre>"+stackTrace+"</pre>";
}else{
return "nothing";
}
}else{
return "";
}}]]></xp:this.value>
</xp:text>
</xp:panel>
<script
type="text/javscript">
<![CDATA[console.log("Hello world...");]]>
</script>
</xp:view>
它的价值:我没有通过搜索 Google 或 Whosebug 找到任何关于这个主题的明确内容。
更新 1:
这是一个咖啡因缺乏或只是没有通过树木看到森林的情况。在您的 HTML 代码中不使用 CDATA 块会有所帮助。我这个懒惰的开发人员尝试在 xp:script 块和 HTML 块之间复制和粘贴,并保留它。现在是 public 在亚特兰大购买 Marky 啤酒的耻辱。
更新 2:
马克选择的饮料可能有危险。虽然我似乎在复制 CDATA 标签时遇到了问题,但问题仍然存在。在我努力生成一个带有错误按钮的简化页面(松散地基于上面提到的来自 OpenLog Logger for XPages ErrorOnClick.xsp 的 XPage)时,我错误地删除了第一个导致我的问题的部分内容地方,局部刷新。当我进行完全刷新时,没问题,但是当我进行部分刷新时,它不会加载。我附上了一个示例页面来触发错误,有两个按钮;一个诱导完整,另一个诱导部分。因此,在完全刷新后,我收到了 "hello world..." 的警报,其中有部分,没有骰子。
MakeSomeError.xsp
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:panel
id="somePanel">
<xp:button
value="Failing Partial"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="somePanel">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button
value="Failing Full"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br />
<xp:text
value="#{viewScope.myStuff}" />
</xp:panel>
</xp:view>
更新 3:
好的。斯文的第二个答案让我非常接近,但出于某种原因,我无法推断出足以让我想要发生的事情发生。我在我的结果下方包含了一个 GIF。我希望发生的唯一不同是我的 Error.xsp(自定义错误 XPage)在遇到错误后继续加载(似乎我需要更改 beforeRenderResponse 阻止 afterRenderResponse 脚本?)。我想附加脚本,而不是替换 Error.xsp 加载。基本上,我试图在错误 XPage 加载后 运行 一个脚本(有一个辅助 JS 文件我试图加载到我的自定义错误 XPage 中,CSS 加载正常,只是不是JS 库)。我愿意:
- 开始工作
- 分享它是什么(这很酷,如果我自己这么说的话)
不要在 script
标签中使用 CDATA
。
<script>
alert('hi Marky!');
</script>
适合我。
此行为的原因是这是一项安全功能。如果通过 Ajax 加载,浏览器不会执行 <script>
块。
但有一个解决方法:
首先你必须添加一个 div 来替换你调用的 XPage:
<div id="errRefresh" />
这只是局部刷新的占位符,否则会失败。
现在,您必须修改错误页面以处理部分刷新。为此,您必须检测它是否是刷新,但您不能使用内置功能(它在错误页面中为空)。所以你必须自己做:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
现在您必须将响应状态代码设置为 200,否则将调用事件中的错误方法:
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
resp.setStatus(200);
然后,您可以添加必须如下所示的 CSJS 块:
<!-- XSP_UPDATE_SCRIPT_START -->
<script>
alert('Hello World!');
</script>
<!-- XSP_UPDATE_SCRIPT_END -->
当部分刷新被处理时,刷新的DOM元素被替换,这就是为什么我们必须用响应重新发送HTML标记,并覆盖X- XspRefreshId 强制替换我们的错误元素:
resp.setHeader('X-XspRefreshId', 'errRefresh' );
最后但同样重要的是,我们必须跳过 JSF 生命周期:
facesContext.responseComplete();
就是这样。
这里是错误页面的beforeRenderResponse事件的完整代码:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = ex.getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "<!-- XSP_UPDATE_SCRIPT_START -->\n" );
writer.write( "<script>\n");
writer.write( "alert('Hello World!' );\n" );
writer.write( "</script>\n");
writer.write( "<!-- XSP_UPDATE_SCRIPT_END -->\n" );
writer.write( "<div id=\"errRefresh\" />\n");
resp.setStatus(200);
resp.setHeader('X-XspRefreshId', 'errRefresh' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
请记住,这可能会导致安全问题。
Marky 还有一个好主意:劫持响应。
这可能是这样的:
<script>
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
var myHandler = function(){
var xhrObj = arguments[1].xhr;
var response = xhrObj.response;
var header = xhrObj.getResponseHeader('X-XspRefreshId');
if( header == "@error" ){
eval( response );
}else{
arguments[1]["error"]( arguments[0], arguments[1]);
}
}
dojo.xhr = function(){
try{
var args = arguments[1];
args["failOk"] = true;
args["error"] = myHandler;
arguments[1] = args;
}catch(e){}
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
</script>
beforeRenderResponse 事件必须这样修改:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "alert('Hello World!' );\n" );
resp.setHeader('X-XspRefreshId', '@error' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
好的,最后一次尝试。一个非常有趣的。在您的 Error.xsp 上,添加以下图片:
<xp:text
escape="true"
id="executeOnAjax"
tagName="img">
<xp:this.attrs>
<xp:attr
name="src"
value="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==">
</xp:attr>
<xp:attr
name="onload"
value="alert('Hello World!');this.parentNode.removeChild(this);">
</xp:attr>
</xp:this.attrs>
<xp:this.rendered>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
refreshId?true:false;}]]>
</xp:this.rendered>
</xp:text>
如果这不符合您的要求,我不明白您要完成什么。
情况
我正在使用自定义错误 XPage,高度偏离 the XSnippet from Tony McGuckin. It works rather well but I would like for the browser to execute a client-side JavaScript block (or load and run a JS file from a given URL). If I navigate directly to the custom error XPage, it loads correctly, but given the nature of how it loads on redirect from a SSJS runtime error, it seems to load any attempts at loading a script block in the head tag, inside the body tag. I've attempted passing through a JS script tag in the body (shown in the code below), attempted using the xp:headTag inside xp:resources, and attempted via an xp:script tag in xp:resources。
浏览器视角
从浏览器的角度来看,在部分刷新期间调用 SSJS 的事件期间遇到 运行 时间错误后,xhr 被调用 returns 500 并将内容设置到正文中标记(屏幕截图)。
查看响应内容时,整个自定义错误 XPage 都在那里,包括 <script type="text/javascript">console.log("hello world");<script>
。这似乎不会触发或向浏览器的 JS 控制台输出任何内容。通过 JS 控制台可见的是来自 dojo 的一些垃圾,抱怨返回一个响应代码为 500 的 XHR(我的 dojoConfig 通过 xsp.client.script.dojo.djConfig 设置为 isDebug: true
在 XSP 属性中)。
问题
在加载自定义错误 XPage 期间发生错误 500 后,是否有办法让客户端 JS 脚本标记在浏览器中加载和执行?
这是我的错误页面的代码。要重现我的结果,调用一个 SSJS 操作导致 运行 时间错误(例如 Paul Withers 的 OpenLog Logger for XPages 项目中包含的 ErrorOnClick XPage)和部分刷新事件。
Error.xsp(在XSP Properties中设置为错误页面)
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/xsp/core xsdxp://localhost/xsp~core.xsd"
pageTitle="${javascript:database.getTitle() + ' | Error'}">
<style
type="text/css"><![CDATA[
body {
background-color: lightblue;
}
form {
width: 1000px !important;
margin: 0 auto !important;
background-color: white !important;
margin-top: 2rem !important;
padding: 0.5rem !important;
height: auto;
}
.xspTextLabel {
font-weight: bold !important;
}
]]></style>
<img
class="logo-simple"
src="//placehold.it/124x32" />
<xp:panel>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
style="font-weight:bold;font-size:12pt"
value="An Unexpected Error Has Occurred:">
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Error:"></xp:label>
<xp:br />
<xp:text
escape="false">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var output = (requestScope.error.toString() || null)+"<br /><br />";
if(requestScope.error instanceof com.ibm.xsp.exception.XSPExceptionInfo){
var codeSnippet = requestScope.error.getErrorText();
var control = requestScope.error.getErrorComponentId();
var cause = requestScope.error.getCause();
output += "In the control : " + control + "<br /><br />";
if(cause instanceof com.ibm.jscript.InterpretException){
var errorLine = cause.getErrorLine();
var errorColumn = cause.getErrorCol();
output += "At line " + errorLine;
output += ", column " + errorColumn + " of:<br />";
}else{
output += "In the script:<br />";
}
if( @Contains(codeSnippet,"#{javascript:") ){
var snipAr = codeSnippet.split("#{javascript:");
var tmpSnip = snipAr[1];
var nwSnip = tmpSnip.substring(0, tmpSnip.length - 1);
output += "#{javascript:<br /><pre>"+nwSnip+"</pre>}"
}else{
output += codeSnippet;
}
}
return output;
}else{
return "";
}}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Stack Trace:"></xp:label>
<xp:br />
<xp:text
escape="false"
style="font-size:10pt">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var stackTrace = "";
var trace = (requestScope.error.getStackTrace() || null);
if(trace != null){
for(var i = 0; i < trace.length; i++){
stackTrace += trace[i] + "<br/>";
}
return "<pre>"+stackTrace+"</pre>";
}else{
return "nothing";
}
}else{
return "";
}}]]></xp:this.value>
</xp:text>
</xp:panel>
<script
type="text/javscript">
<![CDATA[console.log("Hello world...");]]>
</script>
</xp:view>
它的价值:我没有通过搜索 Google 或 Whosebug 找到任何关于这个主题的明确内容。
更新 1: 这是一个咖啡因缺乏或只是没有通过树木看到森林的情况。在您的 HTML 代码中不使用 CDATA 块会有所帮助。我这个懒惰的开发人员尝试在 xp:script 块和 HTML 块之间复制和粘贴,并保留它。现在是 public 在亚特兰大购买 Marky 啤酒的耻辱。
更新 2: 马克选择的饮料可能有危险。虽然我似乎在复制 CDATA 标签时遇到了问题,但问题仍然存在。在我努力生成一个带有错误按钮的简化页面(松散地基于上面提到的来自 OpenLog Logger for XPages ErrorOnClick.xsp 的 XPage)时,我错误地删除了第一个导致我的问题的部分内容地方,局部刷新。当我进行完全刷新时,没问题,但是当我进行部分刷新时,它不会加载。我附上了一个示例页面来触发错误,有两个按钮;一个诱导完整,另一个诱导部分。因此,在完全刷新后,我收到了 "hello world..." 的警报,其中有部分,没有骰子。
MakeSomeError.xsp
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:panel
id="somePanel">
<xp:button
value="Failing Partial"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="somePanel">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button
value="Failing Full"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br />
<xp:text
value="#{viewScope.myStuff}" />
</xp:panel>
</xp:view>
更新 3: 好的。斯文的第二个答案让我非常接近,但出于某种原因,我无法推断出足以让我想要发生的事情发生。我在我的结果下方包含了一个 GIF。我希望发生的唯一不同是我的 Error.xsp(自定义错误 XPage)在遇到错误后继续加载(似乎我需要更改 beforeRenderResponse 阻止 afterRenderResponse 脚本?)。我想附加脚本,而不是替换 Error.xsp 加载。基本上,我试图在错误 XPage 加载后 运行 一个脚本(有一个辅助 JS 文件我试图加载到我的自定义错误 XPage 中,CSS 加载正常,只是不是JS 库)。我愿意:
- 开始工作
- 分享它是什么(这很酷,如果我自己这么说的话)
不要在 script
标签中使用 CDATA
。
<script>
alert('hi Marky!');
</script>
适合我。
此行为的原因是这是一项安全功能。如果通过 Ajax 加载,浏览器不会执行 <script>
块。
但有一个解决方法:
首先你必须添加一个 div 来替换你调用的 XPage:
<div id="errRefresh" />
这只是局部刷新的占位符,否则会失败。
现在,您必须修改错误页面以处理部分刷新。为此,您必须检测它是否是刷新,但您不能使用内置功能(它在错误页面中为空)。所以你必须自己做:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
现在您必须将响应状态代码设置为 200,否则将调用事件中的错误方法:
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
resp.setStatus(200);
然后,您可以添加必须如下所示的 CSJS 块:
<!-- XSP_UPDATE_SCRIPT_START -->
<script>
alert('Hello World!');
</script>
<!-- XSP_UPDATE_SCRIPT_END -->
当部分刷新被处理时,刷新的DOM元素被替换,这就是为什么我们必须用响应重新发送HTML标记,并覆盖X- XspRefreshId 强制替换我们的错误元素:
resp.setHeader('X-XspRefreshId', 'errRefresh' );
最后但同样重要的是,我们必须跳过 JSF 生命周期:
facesContext.responseComplete();
就是这样。
这里是错误页面的beforeRenderResponse事件的完整代码:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = ex.getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "<!-- XSP_UPDATE_SCRIPT_START -->\n" );
writer.write( "<script>\n");
writer.write( "alert('Hello World!' );\n" );
writer.write( "</script>\n");
writer.write( "<!-- XSP_UPDATE_SCRIPT_END -->\n" );
writer.write( "<div id=\"errRefresh\" />\n");
resp.setStatus(200);
resp.setHeader('X-XspRefreshId', 'errRefresh' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
请记住,这可能会导致安全问题。
Marky 还有一个好主意:劫持响应。
这可能是这样的:
<script>
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
var myHandler = function(){
var xhrObj = arguments[1].xhr;
var response = xhrObj.response;
var header = xhrObj.getResponseHeader('X-XspRefreshId');
if( header == "@error" ){
eval( response );
}else{
arguments[1]["error"]( arguments[0], arguments[1]);
}
}
dojo.xhr = function(){
try{
var args = arguments[1];
args["failOk"] = true;
args["error"] = myHandler;
arguments[1] = args;
}catch(e){}
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
</script>
beforeRenderResponse 事件必须这样修改:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "alert('Hello World!' );\n" );
resp.setHeader('X-XspRefreshId', '@error' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
好的,最后一次尝试。一个非常有趣的。在您的 Error.xsp 上,添加以下图片:
<xp:text
escape="true"
id="executeOnAjax"
tagName="img">
<xp:this.attrs>
<xp:attr
name="src"
value="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==">
</xp:attr>
<xp:attr
name="onload"
value="alert('Hello World!');this.parentNode.removeChild(this);">
</xp:attr>
</xp:this.attrs>
<xp:this.rendered>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
refreshId?true:false;}]]>
</xp:this.rendered>
</xp:text>
如果这不符合您的要求,我不明白您要完成什么。