1. 概述

Jersey 是一个使用 Java 开发 Web 服务和客户端的全面开源框架。通过 Jersey,我们可以创建支持完整 HTTP 功能的强大 Web 应用程序。

本文将探讨 Jersey 中的两个特定功能:@FormDataParam@FormParam 注解。尽管这两个注解在本质上相似,但它们之间存在显著差异,如下所述。

2. 背景

客户端与服务器之间交换数据的方式有很多种,XML 和 JSON 是其中最受欢迎的,但并非唯一选择。当客户端是网页时,另一种数据格式是表单编码数据。这是因为 HTML 使得定义包含各种输入(文本、复选框、下拉列表等)的表单变得简单,并将这些数据发送到远程服务器。

这就是 @FormDataParam@FormParam 注解的作用之处。它们都在 Jersey 服务器应用程序中用于处理表单数据,但有一些重要的区别。

现在我们了解了这两个注解的背景,接下来我们将看看如何使用它们以及它们的一些差异。

3. 使用 @FormParam

简而言之,当 API 预期 URL 编码数据时,我们会使用 @FormParam 注解。让我们看一个只有文本字段的简单 HTML 表单:

<form method="post" action="/example1">
    <input name="first_name" type="text">
    <input name="last_name" type="text">
    <input name="age" type="text">
    <input type="submit">
</form>

这个表单有三个字段:名字、姓氏和年龄。它使用 HTTP POST 方法将表单数据发送到 /example1 的 URL。

要在 Jersey 中处理此表单,我们在服务器应用中定义以下端点:

@POST
@Path("/example1")
public String example1(
  @FormParam("first_name") String firstName,
  @FormParam("last_name") String lastName,
  @FormParam("age") String age)
{
    // process form data
}

请注意,我们在 HTML 表单的每个字段上都使用了注解。无论表单有多少或何种类型的字段,这都是一样的。通过使用 @FormParam 注解,Jersey 将表单值绑定到相应的方法参数。

实际上,表单编码数据对应于 MIME 类型 application/x-www-form-urlencoded。在浏览器和我们的服务之间发送的数据背后,看起来像这样:

first_name=john&last_name=smith&age=42

这种做法的好处在于数据处理起来相对容易,因为它只涉及文本,没有二进制数据。此外,由于字符串可以作为查询参数在 URL 中传递,这种方法也可以使用 HTTP GET 方法。然而,通过 URL 传递表单数据时应谨慎,因为可能包含敏感信息。

4. 使用 @FormDataParam

相比之下,@FormDataParam 注解更为灵活,可以处理任何组合的文本和二进制数据。在实践中,这对于通过 HTML 表单上传文件非常有用。

让我们看一个使用 @FormDataParam 注解的例子。首先,我们需要将 jersey-media-multipart 模块从 Maven 中心仓库 导入到项目中:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>3.1.3</version>
</dependency>

现在,让我们在上面创建的 HTML 表单中添加一个文件上传字段:

<form method="post" action="/example2" enctype="multipart/form-data >
    <input name="first_name" type="text">
    <input name="last_name" type="text">
    <input name="age" type="text">
    <input name="photo" type="file">
    <input type="submit">
</form>

注意,我们现在在表单元素中指定了编码类型 multipart/form-data。这是必要的,以便浏览器以我们的服务期望的格式发送数据。

接下来,在应用中,我们创建以下端点来处理来自此表单的数据:

@POST
@Path("/example2")
public String example2(
  @FormDataParam("first_name") String firstName,
  @FormDataParam("last_name") String lastName,
  @FormDataParam("age") String age,
  @FormDataParam("photo") InputStream photo)
{
    // handle form data
}

注意到每个表单字段现在都使用了 @FormDataParam 注解。我们还使用 InputStream 类型作为参数类型,用于处理二进制数据。

与前一个示例不同,每个字段及其值不再组合成字符串,而是组合成部分。每个部分由唯一的令牌分隔,使 Jersey 能轻松识别每个参数并将每个部分绑定到相应的方法参数。

例如,从这个表单发送到我们服务的原始消息可能看起来像这样:

------WebKitFormBoundarytCyB57mkvJedAHFx
Content-Disposition: form-data; name="first_name"

John
------WebKitFormBoundarytCyB57mkvJedAHFx
Content-Disposition: form-data; name="last_name"

Smith
------WebKitFormBoundarytCyB57mkvJedAHFx
Content-Disposition: form-data; name="age"

42
------WebKitFormBoundarytCyB57mkvJedAHFx
Content-Disposition: form-data; name="photo"; filename="john-smith-profile.jpeg"
Content-Type: image/jpeg


------WebKitFormBoundarytCyB57mkvJedAHFx--

这种做法的主要好处是可以一起发送文本和二进制数据。然而,与传统表单编码数据相比,它也有一些缺点。首先,只能使用 HTTP POST 方法。这是因为二进制数据不能被编码并通过 URL 字符串传递。其次,负载大小更大。如上所示,为了定义分隔符和头信息所需的额外文本,增加了比常规表单编码数据更大的体积。

5. 总结

在这篇文章中,我们研究了 Jersey 库中的两个不同注解:@FormParam@FormDataParam这两个注解在应用程序中处理表单数据,但我们看到,它们各有不同的用途。

@FormParam 更适合发送表单编码数据,可以使用 GET 或 POST 方法,但仅限于文本字段。相反,@FormDataParam 注解处理多部分数据,包括文本和二进制数据,但仅适用于 POST 方法。

本文的所有代码示例均位于 GitHub 上