jjzjj

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

coder 2024-06-27 原文

我一直在开发一个 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 代码中,我已经在代码末尾关闭了所有流和套接字。是否有必要关闭所有这些或其中的一些可以完成清理工作?

最佳答案

它是十六进制的 block 长度。看,响应正文按照以下 header 以 block 的形式发送:

Transfer-Encoding: chunked

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

当内容长度未知并且客户端已将自己标识为支持 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 在每个请求的基础上处理实例变量。这不是线程安全的。将这些实例变量移动到方法 block 内。有关详细信息,另请参阅 How do servlets work? Instantiation, sessions, shared variables and multithreading .

关于java - 通过 Java Servlet 发送 XML 数据时发送不需要的字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31462367/

有关java - 通过 Java Servlet 发送 XML 数据时发送不需要的字符的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - unicode 字符串的长度 - 2

    在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

  5. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  6. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  7. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  8. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  9. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  10. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

随机推荐