【问题标题】:Generate an HTML page, and open in a new window, from a Vaadin 8 app从 Vaadin 8 应用程序生成 HTML 页面并在新窗口中打开
【发布时间】:2019-01-12 10:25:17
【问题描述】:

在我的 Vaadin 8 网络应用程序中,我希望用户能够通过单击按钮在另一个窗口中打开报告。内容将由 Vaadin 应用程序使用纯 HTML5 生成,而不是使用 Vaadin 小部件。

Vaadin 8 手册有一个页面Handling Browser Windows。它显示了使用BrowserWindowOpener 对象打开一个新窗口。但是该窗口包含一个 Vaadin UI 子类,而我想生成自己的 HTML 内容。

传递数据库标识符值等信息的奖励积分。

【问题讨论】:

    标签: java html window vaadin vaadin8


    【解决方案1】:

    这是一个完整的示例应用程序,内置于 Vaadin 8.5.1。我们在TextField 中将 UUID 显示为文本,并带有一个按钮,该按钮打开第二个窗口,显示一个网页,其中包含由我们的 Vaadin 应用程序生成的 HTML,而不使用 Vaadin 小部件或布局。该字段的 id 被传递到新窗口,在实际应用中可用于数据库查找。

    如手册中该页所示,您确实需要使用BrowserWindowOpener(或Link)。这必须提前配置,用户点击按钮之前,因为浏览器常见的安全限制。因此,我们必须提前配置BrowserWindowOpener 对象,并与按钮关联,而不是在按钮的点击监听器中编写代码。

    定义用户单击以生成报告的按钮。

    Button webPageButton = new Button( "Generate Person report" );
    

    定义要打开的新窗口的目的地,它应该使用哪个 URL 作为其网址。我们想回调我们的 Vaadin 应用程序。因此,在运行时获取此 Web 应用程序的 URL。在 Java Servlet 术语中,我们的 Web 应用程序的技术术语是“上下文”。所以我们向当前上下文询问它的 URL(路径)。

    String servletPath = VaadinServlet.getCurrent().getServletContext().getContextPath(); // URL for this web app at runtime.
    

    我们需要将该 URL 缩小到我们的报告,详细说明要从数据库加载的单个 Person 对象。因此,我们发明了person.html 作为我们 URL 中请求的资源。

    我们希望在不调用 Vaadin 小部件的情况下请求动态生成的 HTML 页面,因此我们使用 ExternalResource 类。

    Resource resource = new ExternalResource( servletPath + "/person.html" );  // Defining an external resource as a URL that is not really so external -- will call back into this same web app.
    

    有了Resource 对象,我们就可以定义BrowserWindowOpener了。

    BrowserWindowOpener webPageOpener = new BrowserWindowOpener( resource );
    

    我们来配置它的一些属性,比如要打开的窗口的标题。

    webPageOpener.setWindowName( "Person ID: " + personUuid.getValue() );  // Set title of the new window to be opened.
    

    我们希望传递要从数据库中检索的“人”行的 ID,然后显示在我们生成的网页中。

    一种将诸如此类的信息作为参数传递到 URL 上的 query string 中的一种方法。所以我们 URL 的最后一部分看起来像person.html?person_id= f0e32ddc-18ed-432c-950b-eda3f3e4a80d。该值必须是文本值,因此我们使用代表 UUID 的 128 位的规范 36 字符十六进制字符串作为我们的数据库标识符。我们给这个值一个任意的键名,例如person_id

    String param = "person_id";
    webPageOpener.setParameter( param , personUuid.getValue() );
    

    我们可以设置要打开的新窗口的大小。我们将使其在运行时与用户当前窗口的大小相同。我们将使窗口可调整大小,以便用户可以将其拉伸得更大或更小。我们希望以width=800,height=600,resizable 之类的字符串描述窗口特征。我们将在运行时插入该宽度和高度。

    String windowFeaturesString = String.format( "width=%d,height=%d,resizable" , Page.getCurrent().getBrowserWindowWidth() , Page.getCurrent().getBrowserWindowHeight() ) ; // Same size as original window.
    webPageOpener.setFeatures( windowFeaturesString );  // Example: "width=800,height=600,resizable".
    

    我们已经完成了要打开的新窗口的配置。由于在事件侦听器中用户单击按钮无法调用打开窗口,而其他行为通常会这样做,因此我们必须提前将打开器与按钮相关联。

    webPageOpener.extend( webPageButton ); // Associate opener with button.
    

    为了好玩,我们可以预览要被新窗口调用的 URL。在实际工作中,这里使用 SLF4JLogBack 等日志框架。对于这个演示,我们转储到控制台。

    System.out.println( "TRACE BrowserWindowOpener URL: " + webPageOpener.getUrl() );
    

    好的,我们现在设置了一个带有开启器的按钮,用于请求生成基于 HTML 的报告。接下来我们必须生成该报告。为此,请告诉我们的 Vaadin 应用程序期待带有我们上面指定的 person.html URL 的传入 URL。我们通过实现RequestHandler 接口来做到这一点。见the manual

    在我们的RequestHandler 中,我们做了四件事:

    1. 检索在新窗口中打开的 URL 的查询字符串中作为参数传递的 UUID 的十六进制字符串。
    2. 从该十六进制字符串重构UUID 对象。
    3. UUID 对象传递给生成要在此新窗口中显示的 HTML 的例程。
    4. 通过将 HTML 传递给 VaadinResponse 对象,在新窗口中显示该 HTML,该对象通过 Java Servlet 技术传递回用户的 Web 浏览器。

    我们必须实例化我们的RequestHandler 实现,并将实例注册到用户的会话中,VaadinSession 对象。

    VaadinSession.getCurrent().addRequestHandler(
            new RequestHandler() {
                @Override
                public boolean handleRequest ( VaadinSession session ,
                                               VaadinRequest request ,
                                               VaadinResponse response )
                        throws IOException {
                    if ( "/panel.html".equals( request.getPathInfo() ) ) {
                        // Retrieve the hex-string of the UUID from the URL’s query string parameter.
                        String uuidString = request.getParameter( "person_id" );  // In real-work, validate the results here.
                        UUID uuid = UUID.fromString( uuidString ); // Reconstitute a `UUID` object from that hex-string. In real-work, validate the results here.
                        System.out.println( "UUID object reconstituted from string passed as parameter in query string of URL opened in new window: " + uuid );
                        // Build HTML.
                        String html = renderHtml( uuid );
                        // Send out the generated text as HTML, in UTF-8 character encoding.
                        response.setContentType( "text/html; charset=utf-8" );
                        response.getWriter().append( html );
                        return true; // We wrote a response
                    } else
                        return false; // No response was written
                }
            } );
    

    填写该方法以生成 HTML。

    // Generate the HTML to report on the details of a `person` from the database, given the UUID of that database row.
    private String renderHtml ( UUID uuid ) {
        String eol = "\n"; // End-of-line character(s) to use in the HTML.
        StringBuilder html = new StringBuilder();
        html.append( "<!DOCTYPE html>" ).append( eol );
        html.append( "<html>" ).append( eol );
        html.append( "<head>" ).append( eol );
        html.append( "<title>Person</title>" ).append( eol );
        html.append( "</head>" ).append( eol );
        html.append( "<body style='color:DarkSlateGray' >" ).append( eol );
        html.append( "<h1>Demo</h1>" ).append( eol );
        html.append( "<p>This is a drill. This is only a drill.</p>" ).append( eol );
        html.append( "<p>If this had been a real application, you would have seen some data.</p>" ).append( eol );
        html.append( "<p>Person ID: " ).append( uuid.toString() ).append( ".</p>" ).append( eol );
        html.append( "<p style='color:DimGray ; font-family: Pragmata Hack Menlo monospaced' >Report generated " ).append( Instant.now() ).append( ".</p>" ).append( eol );
        html.append( "</body>" ).append( eol );
        html.append( "</html>" ).append( eol );
        String s = html.toString();
        return s;
    }
    

    生成的 HTML 源代码如下所示:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Person</title>
    </head>
    <body style='color:DarkSlateGray' >
    <h1>Demo</h1>
    <p>This is a drill. This is only a drill.</p>
    <p>If this had been a real application, you would have seen some data.</p>
    <p>Person ID: cc5e975b-2632-4c92-a1cb-b25085c60e60.</p>
    <p style='color:DimGray ; font-family: Pragmata , Hack , Menlo , monospace' >Report generated 2018-08-05T02:33:13.028594Z.</p>
    </body>
    </html>
    

    为方便起见,这里是整个 Vaadin 8 应用程序,MyUI.java 文件的内容首先由 Vaadin Ltd 公司提供的最简单的 Maven 原型生成。

    package com.basilbourque.example;
    
    import javax.servlet.annotation.WebServlet;
    
    import com.vaadin.annotations.Theme;
    import com.vaadin.annotations.VaadinServletConfiguration;
    import com.vaadin.server.*;
    import com.vaadin.ui.Button;
    import com.vaadin.ui.Label;
    import com.vaadin.ui.TextField;
    import com.vaadin.ui.UI;
    import com.vaadin.ui.VerticalLayout;
    
    import java.io.IOException;
    import java.time.Instant;
    import java.time.ZonedDateTime;
    import java.util.UUID;
    
    /**
     * This UI is the application entry point. A UI may either represent a browser window
     * (or tab) or some part of an HTML page where a Vaadin application is embedded.
     * <p>
     * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
     * overridden to add component to the user interface and initialize non-component functionality.
     */
    @Theme ( "mytheme" )
    public class MyUI extends UI {
    
        @Override
        protected void init ( VaadinRequest vaadinRequest ) {
            final VerticalLayout layout = new VerticalLayout();
    
            TextField personUuid = new TextField( "UUID of Person:" );
            personUuid.setWidth( 22 , Unit.EM );
            personUuid.setValue( UUID.randomUUID().toString() );
            personUuid.setReadOnly( true );
    
            Button webPageButton = new Button( "Generate Person report" );
            webPageButton.setWidthUndefined();
            webPageButton.addClickListener( e -> {
                System.out.println( "Button clicked. " + ZonedDateTime.now() );
            } );
    
            // Configure web page opener object. Must be done *before* user clicks on button, not after.
            String servletPath = VaadinServlet.getCurrent().getServletContext().getContextPath(); // URL for this web app at runtime.
            Resource resource = new ExternalResource( servletPath + "/person.html" );  // Defining an external resource as a URL that is not really so external -- will call back into this same web app.
            BrowserWindowOpener webPageOpener = new BrowserWindowOpener( resource );
            webPageOpener.setWindowName( "Person ID: " + personUuid.getValue() );  // Set title of the new window to be opened.
            String param = "person_id";
            webPageOpener.setParameter( param , personUuid.getValue() );
            String windowFeaturesString = String.format( "width=%d,height=%d,resizable" , Page.getCurrent().getBrowserWindowWidth() , Page.getCurrent().getBrowserWindowHeight() ); // Same size as original window.
            webPageOpener.setFeatures( windowFeaturesString );  // Example: "width=800,height=600,resizable".
            webPageOpener.extend( webPageButton ); // Connect opener with button.
            System.out.println( "TRACE BrowserWindowOpener URL: " + webPageOpener.getUrl() );
    
            layout.addComponents( personUuid , webPageButton );
            setContent( layout );
    
            // A request handler for generating some content
            VaadinSession.getCurrent().addRequestHandler(
                    new RequestHandler() {
                        @Override
                        public boolean handleRequest ( VaadinSession session ,
                                                       VaadinRequest request ,
                                                       VaadinResponse response )
                                throws IOException {
                            if ( "/person.html".equals( request.getPathInfo() ) ) {
                                // Retrieve the hex-string of the UUID from the URL’s query string parameter.
                                String uuidString = request.getParameter( "person_id" );  // In real-work, validate the results here.
                                UUID uuid = UUID.fromString( uuidString ); // Reconstitute a `UUID` object from that hex-string. In real-work, validate the results here.
                                System.out.println( "UUID object reconstituted from string passed as parameter in query string of URL opened in new window: " + uuid );
                                // Build HTML.
                                String html = renderHtml( uuid );
                                // Send out the generated text as HTML, in UTF-8 character encoding.
                                response.setContentType( "text/html; charset=utf-8" );
                                response.getWriter().append( html );
                                return true; // We wrote a response
                            } else
                                return false; // No response was written
                        }
                    } );
        }
    
        // Generate the HTML to report on the details of a `person` from the database, given the UUID of that database row.
        private String renderHtml ( UUID uuid ) {
            String eol = "\n"; // End-of-line character(s) to use in the HTML.
            StringBuilder html = new StringBuilder();
            html.append( "<!DOCTYPE html>" ).append( eol );
            html.append( "<html>" ).append( eol );
            html.append( "<head>" ).append( eol );
            html.append( "<title>Person</title>" ).append( eol );
            html.append( "</head>" ).append( eol );
            html.append( "<body style='color:DarkSlateGray' >" ).append( eol );
            html.append( "<h1>Demo</h1>" ).append( eol );
            html.append( "<p>This is a drill. This is only a drill.</p>" ).append( eol );
            html.append( "<p>If this had been a real application, you would have seen some data.</p>" ).append( eol );
            html.append( "<p>Person ID: " ).append( uuid.toString() ).append( ".</p>" ).append( eol );
            html.append( "<p style='color:DimGray ; font-family: Pragmata , Hack , Menlo , monospace' >Report generated " ).append( Instant.now() ).append( ".</p>" ).append( eol );
            html.append( "</body>" ).append( eol );
            html.append( "</html>" ).append( eol );
            String s = html.toString();
            System.out.println( "\n\n" + s + "\n\n" );
            return s;
        }
    
        @WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
        @VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
        public static class MyUIServlet extends VaadinServlet {
        }
    }
    

    【讨论】:

      【解决方案2】:

      您也可以通过将 html 字符串作为参数传递给 BrowserWindowOpener 的 UI 类来做到这一点:

      BrowserWindowOpener opener = new BrowserWindowOpener(MyUI.class);
      opener.extend(myButtonForLaunchingNewWindow);
      opener.setParameter("text",myHtmlStringWhichIJustGenerated);
      
      public static class MyUI extends UI {
          @Override
          protected void init(VaadinRequest request) {
      
              String text = request.getParameter("text");
              // Have some content to print
              setContent(new Label(
                      text,
                      ContentMode.HTML));
          }
      }
      

      编辑:使用我以前的方法时,我遇到了一个问题,即由于 HTTP 400 错误而未显示弹出页面。这是由于 http 标头太大(是的,我生成了一个大的 html 页面)。

      我的解决方案是直接为 BrowserWindowOpener 生成 StreamResource:

      StreamResource streamResource = new StreamResource((StreamResource.StreamSource) () -> 
          new ByteArrayInputStream(htmlString.getBytes()), "report.html");
      BrowserWindowOpener opener = new BrowserWindowOpener(streamResource);
      opener.extend(myButtonForLaunchingNewWindow);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-03
        • 2017-03-29
        • 2015-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-17
        • 1970-01-01
        相关资源
        最近更新 更多