通过 Java Servlet 发送 XML 数据时发送了不需要的字符

Unwanted characters sent while sending XML data via Java Servlets

我一直在开发一个 Java 网络应用程序,它仅通过 HTML 形式获取 first_namemiddle_namelast_name 参数,然后将其嵌入数据到 XML 文件中并返回给客户端。

我设置了Content-Type: text/xml

这是我的 servlet 代码:

package com.adi.request.xml;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestToXMLServlet extends HttpServlet {

    private String lastName;
    private String firstName;
    private String middleName;

    /* Request Handling... */

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {

        setName(request);  // Initialising the firstName, middleName and lastName
        String xmlDoc = getXML();  // Build and recieve the XML output

        response.setContentType("text/xml");  // TO BE NOTED...

        try(PrintWriter writer = response.getWriter()) {

            writer.print(xmlDoc);  // Printing the XML output
            writer.flush();
        } catch(IOException e) {

            e.printStackTrace();
        }
    }   


    // Setting the firstName, middleName and lastName
    private void setName(HttpServletRequest request) {

        firstName = request.getParameter("first_name");
        lastName = request.getParameter("last_name");
        middleName = request.getParameter("middle_name");
    }

    // Building the XML output
    private String getXML() {
        // The append() methods just adds a \r\n at the end of every line.
        String xmlDoc = append("<?xml version=\"1.0\" encoding=\"utf-8\"?>")+
                        append("<Request>")+
                        append("    <FirstName>"+firstName+"</FirstName>")+
                        append("    <MiddleName>"+middleName+"</MiddleName>")+
                        append("    <LastName>"+lastName+"</LastName>")+
                        append("</Request>");

        return xmlDoc;              
    }

    private String append(String str) {

        return str + "\r\n";
    }
}  

HTML形式:

<!DOCTYPE html>
<html> 
<head>
 <title>Request to XML - Servlet</title>
</head>

<body>
  <form method="GET" action="Request.do">
   <label for="first_name">Firstname:</label>
   <input type="text" name="first_name" id="first_name" />
   
   <br>
   
   <label for="middle_name">Middlename</Label>
   <input type="text" name="middle_name" id="middle_name" />
   
   <br>
   
   <label for="last_name">Lastname</Label>
   <input type="text" name="last_name" id="last_name" />
   
   <br>
   
   <input type="submit" name="submit" value="GET" />
  </form>
 </body>
</html>

这工作正常,我的浏览器正确显示 XML 格式的数据。

问题在于

我写了一个小 jython 应用程序,它使用原始套接字向上面写的 Java Servlet 发出 HTTP POST 请求。虽然它接收到正确的 XML 格式数据,但它也会在实际需要的 XML 数据的开头和结尾接收不需要的字符。

这是我的 jython 代码:

from java.io import *
from java.net import *
from java.util import *

sock = Socket("localhost", 8080)

ostream = sock.getOutputStream()
writer = PrintWriter(ostream)

params="first_name=Aditya&middle_name=Rameshwarpratap&last_name=Singh"

writer.print("GET /RequestToXML/Request.do?"+params+" HTTP/1.1\r\n")
writer.print("Host: localhost:8080\r\n")
writer.print("Connection: Close\r\n")
writer.print("\r\n")
writer.flush()

istream = sock.getInputStream()
scanner = Scanner(istream)

while(scanner.hasNextLine()):
    print(scanner.nextLine())

istream.close()
ostream.close()
scanner.close()
writer.close()
sock.close()  

这段代码的输出是:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Thu, 16 Jul 2015 18:46:37 GMT
Connection: close

bc  // What is this?    
<?xml version="1.0" encoding="utf-8"?>
<Request type="POST">
    <FirstName>Aditya</FirstName>
    <MiddleName>Rameshwarpratap</MiddleName>
    <LastName>Singh</LastName>
</Request>

0  // And this?

所以我的问题是:

  1. 这些字符是什么?为什么在内容类型为 text/xml 时还要发送这些字符?

  2. 这无关紧要,但在我的 jython 代码中,我已经在代码末尾关闭了所有流和套接字。是否有必要关闭所有这些或其中的一些可以完成清理工作?

这是十六进制的块长度。看,响应 body 正在按以下 header:

分块发送

Transfer-Encoding: chunked

有关此传输编码的更多详细信息,请参阅 Wikipedia。带有 bc 的行表示 188 字节长的块的开始 (0xBC = 188)。带有 0 的行表示终止块(因此客户端知道它可以停止读取并且不需要等待具有剩余内容的新块,以防连接设置为保持活动状态)。

当内容长度未知并且客户端已将自己标识为支持 HTTP 1.1 的客户端时,servletcontainer 将自动切换到分块编码。它甚至在 doGet():

的 javadoc 中明确提到

...

Where possible, set the Content-Length header (with the ServletResponse.setContentLength(int) method), to allow the servlet container to use a persistent connection to return its response to the client, improving performance. The content length is automatically set if the entire response fits inside the response buffer.

When using HTTP 1.1 chunked encoding (which means that the response has a Transfer-Encoding header), do not set the Content-Length header.

...

您的客户端未以能够使用分块响应的方式编写。它基本上是一个非常基本的套接字,在请求中 header 伪装成 HTTP 1.1 客户端。

如果无法负担重写客户端以使其能够处理它(至少尝试假装成 HTTP 1.0 客户端),或者切换到真正的 1.1 HTTP 感知客户端(在 Java 术语,例如 URLConnection),然后以设置内容长度的方式重写您的 servlet。

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
    // ...
    String xmlDoc = getXML();
    byte[] content = xmlDoc.getBytes("UTF-8");

    response.setContentType("text/xml");
    response.setCharacterEncoding("UTF-8");
    response.setContentLengthLong(content.length);
    response.getOutputStream().write(content);
}   

如果你不是 Java EE 7 / Servlet 3.1 yet,你可以保证 XML 内容不大于 Integer.MAX_VALUE (2GB ), 然后使用

    response.setContentLength((int) content.length);

或者如果你不能保证,那么使用

    response.setHeader("Content-Length", String.valueOf(content.length));

请注意,它必须代表字节长度,因此肯定不是字符(字符串)长度。另请注意,您不需要 try-with-resources 语句。容器会自己担心冲洗和关闭。

另请参阅:


与具体问题无关,您的 servlet 正在 per-request 基础上处理实例变量。这不是线程安全的。将这些实例变量移动到方法块内。有关详细信息,另请参阅 How do servlets work? Instantiation, sessions, shared variables and multithreading.