JAX-RX常用的注解:
@javax.ws.rs.Path // 请求的资源类或资源方法的uri路径
@javax.ws.rs.GET //表示此方法响应HTTP GET请求。
@javax.ws.rs.POST // 表示此方法响应HTTP POST请求。
@javax.ws.rs.PUT // 通常用来更新数据,PUT操作
@javax.ws.rs.DELETE // 通常用来删除数据。
@javax.ws.rs.Produces //设置Http返回报文,报文体的内容类型
@javax.ws.rs.Consumes //客户端请求的MIME媒体类型
@javax.ws.rs.QueryParam // 一般是GET请求的参数,相当于SpringMVC框架的@RequestParam
@javax.ws.rs.FormParam // 媒体类型为”application/x-www-form-urlencoded” 的参数
@javax.ws.rs.PathParam // uri中指定的路径参数绑定到资源方法参数
了解了Jboss的Easyrest后,我们通过一个文件上传的例子来搭建一个RESTFul项目,基于SpringBoot2.0,采用了开源的starter组件resteasy-spring-boot
,GitHub链接:https://github.com/resteasy/resteasy-spring-boot
在IDEA里new一个project,这里使用Spring Initializer
快速创建一个SpringBoot项目,Server url可以使用Spring官网的,也可以使用阿里的,然后点击Next
选择jdk版本和maven
因为有些jar在start.spring.io
里没集成,所以需要自己加上,可以参考我的配置,注意,这个resteasy-spring-boot-starter
版本尽量高点,因为要和SpringBoot2.0版本兼容,使用降低版本可能会出现如下问题java.lang.IllegalArgumentException: Could not find class [org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration]
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>springboot-jboss-uploadfile</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-jboss-uploadfile</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<springboot.version>2.2.1.RELEASE</springboot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--actuator监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
</dependency>
<!--resteasy-spring-boot-starter -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring-boot-starter</artifactId>
<scope>runtime</scope>
<version>3.3.2.Final</version>
</dependency>
<!-- 文件上传需要 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>3.9.1.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>jboss-releases-repository</id>
<name>JBoss Releases Repository</name>
<url>https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</project>
定义一个JAX-RS的Application类,使用@ApplicationPath
定义根路径,一定要加@Component
才能被Spring容器扫描到
package com.example.jbossuploadfile.configuration;
import org.springframework.stereotype.Component;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@Component
@ApplicationPath("/rest/")
public class JAXRSApplication extends Application {
}
application.yml配置文件,定义JAXRSApplication
类的路径
server:
port: 8080
spring:
main:
banner-mode: "off"
resteasy:
jaxrs:
app:
registration: property
classes: com.example.jbossuploadfile.configuration.JAXRSApplication
management:
endpoints:
web:
exposure:
include:
- health
- shutdown
endpoint:
shutdown:
enabled: true
logging:
level:
org:
springframework: info
然后可以写一个文件上传的例子,定义一个返回的实体类
package com.example.jbossuploadfile.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FileUploadResult implements Serializable {
private String fileUrl;
private Long fileSize;
private String fileName;
private String fileType;
}
使用MultipartFormDataInput
来实现文件上传,注意客户端传入的媒体格式,要定义为MediaType.MULTIPART_FORM_DATA
,也就是form-data
,才能支持文件上传
package com.example.jbossuploadfile.endpoint;
import cn.hutool.core.io.FileUtil;
import com.example.jbossuploadfile.entity.FileUploadResult;
import lombok.extern.slf4j.Slf4j;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.springframework.stereotype.Component;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
@Slf4j
@Path("/api")
@Component
public class FileUploadEndpoint {
@Path("/v1/fileUpload")
@POST
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.MULTIPART_FORM_DATA})
public FileUploadResult filepload(MultipartFormDataInput formDataInput) throws IOException {
String fileName = getFileName(formDataInput , "file");
String fileType = fileName.substring(fileName.lastIndexOf("."));
InputStream inputStream = getInputStream(formDataInput , "file");
File file = FileUtil.writeFromStream(inputStream, new File("D:/server/" + fileName));
long length = file.length();
log.info("fileName : [{}] , fileTye : [{}], size:[{}]" , fileName , fileType , length);
return FileUploadResult.builder()
.fileName(fileName)
.fileUrl(file.getPath())
.fileSize(length)
.fileType(fileType).build();
}
private InputStream getInputStream(MultipartFormDataInput input, String s) {
try {
InputStream result;
if (input.getParts().size() == 1) {
InputPart filePart = input.getParts().iterator().next();
result = filePart.getBody(InputStream.class, null);
} else {
result = input.getFormDataPart(s, ByteArrayInputStream.class, null);
}
if (result == null) {
throw new IllegalArgumentException("Can't find a valid 'file' part in the multipart request");
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException("Error while reading multipart request", e);
}
}
private String getFileName(MultipartFormDataInput input , String s) {
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
List<InputPart> inputParts = uploadForm.get(s);
for (InputPart inputPart : inputParts) {
MultivaluedMap<String, String> header = inputPart.getHeaders();
String[] contentDisposition = header.getFirst("Content-Disposition").split(";");
for (String filename : contentDisposition) {
if ((filename.trim().startsWith("filename"))) {
String[] name = filename.split("=");
String finalFileName = name[1].trim().replaceAll("\"", "");
return finalFileName;
}
}
}
return "unknown";
}
}
在Postman里测试一下,注意使用form-data
方式