为了实现带有附加元信息的文件上载endpoint,我编写了以下@RestController
:
@RestController
@RequestMapping(Resource.ROOT)
@AllArgsConstructor(onConstructor = @__({@Inject}))
public class Resource {
public static final String ROOT = "/test";
private final Logger logger;
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.APPLICATION_JSON)
public ResponseEntity<Void> test(@Valid final Request request) {
logger.info("request = {}", request);
return ResponseEntity.ok().build();
}
}
request
指定为:
@Value
@AllArgsConstructor
public class Request {
@NotNull
String name;
@NotNull
MultipartFile file;
}
和一个快乐小路测试:
@SpringBootTest
@AutoConfigureMockMvc
class TestCase {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnOk() throws Exception {
// GIVEN
final byte[] content = Files.readAllBytes(Path.of(".", "src/test/resources/PenPen.png"));
final String name = "name";
// WHEN
// @formatter:off
mockMvc.perform(MockMvcRequestBuilders
.multipart(Resource.ROOT)
.file("file", content)
.param("name", name))
// THEN
.andExpect(status().isOk());
// @formatter:on
}
}
运行测试(./mvnw test)
时,它失败,endpoint返回400错误请求
而不是200确定
。读取日志显示请求参数file
为null
:
...
Content type = text/plain;charset=UTF-8
Body = file: must not be null.
...
我部分理解为什么它是空
。有了这些部分知识,我可以通过使request
中的字段file
可变来规避这个问题:
@ToString
@Getter
@AllArgsConstructor
public class Request {
@NotNull
private final String name;
@Setter
@NotNull
private MultipartFile file;
}
“修复”问题的代码可以在Bitbucket,branchproblem-solled-by-making-field-mutable
上找到。
但是,这使得request
变了,我想防止这种情况发生。为了进一步研究,我展开了request
上的lombok注释,并添加了一些日志记录:
public class Request {
private static final Logger LOGGER = LoggerFactory.getLogger(Request.class);
@NotNull
private final String name;
@NotNull
private MultipartFile file;
public Request(final String name, final MultipartFile file) {
this.name = name;
this.setFile(file);
}
public @NotNull String getName() {
return this.name;
}
public @NotNull MultipartFile getFile() {
return this.file;
}
public String toString() {
return "Request(name=" + this.getName() + ", file=" + this.getFile() + ")";
}
public void setFile(final MultipartFile file) {
LOGGER.info("file = {}", file);
this.file = file;
}
}
展开版本的代码可以在Bitbucket,分支lombok-unrolled-for-debugging
上找到。
在查看现在成功的测试的日志语句时,我们可以看到request::setfile
被调用了两次:
2020-09-05 09:42:31.049 INFO 11012 --- [ main] d.turing85.springboot.multipart.Request : file = null
2020-09-05 09:42:31.056 INFO 11012 --- [ main] d.turing85.springboot.multipart.Request : file = org.springframework.mock.web
第一个调用来自构造函数调用。我想第二个调用来自Spring表单参数映射机制中的某个地方。
我知道有可能在endpoint上单独定义表单参数,并在方法中构造request
实例:
public class Resource {
...
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.APPLICATION_JSON)
public ResponseEntity<Void> test(
@RequestPart(name = "name") final String name,
@RequestPart(name = "file") final MultipartFile file) {
final Request request = new Request(name, file);
logger.info("request = {}", request);
return ResponseEntity.ok().build();
}
}
然而,这将导致其他问题。例如,我们必须为MissingServletRequestPartException
添加一个额外的异常映射器,并将返回的HTTP响应与Bindexception
的现有响应对齐。如果可能的话,我想避免这样做。
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> test(@Valid @ModelAttribute final RequestDto request) {
return ResponseEntity.ok().build();
}
它仍然在使用rest api调用。但我真的没有得到你的一成不变的关心。
如果定义了多部分数据setter,则可以使用modelAttribute。
@SpringBootTest
@AutoConfigureMockMvc
class FileUploadControllerIT {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnOk() throws Exception {
// GIVEN
final byte[] content = Files.readAllBytes(Paths.get(Thread.currentThread().getContextClassLoader().getResource("text.txt").toURI()));
final String name = "name";
// WHEN
// @formatter:off
mockMvc.perform(MockMvcRequestBuilders
.multipart("/context/api/v1")
.file("multipartFile", content)
.param("name", name))
// THEN
.andExpect(status().isOk());
// @formatter:on
}
}
上面的代码与ModelAttribute一起工作。
当创建Gio controller对象时,可以将配置参数传递给controller,具体传递方式如下所示: var configs = { color: { surface:0xFF0000 } }; var globe = new Gio.controller(container, configs);
我目前正在使用java编写线性代数库,灵感来自我的线性代数I 我希望我的程序尽可能灵活,因此决定我的矩阵类将有许多可用的构造函数。然而,当我试图将这两个构造函数结合在一起时,Java抛出了一个编译错误: 我不明白为什么,因为第一个构造函数将接受任意数量的数组作为args:而第二个构造函数将只接收一个arg:。我意识到在两个构造函数中,我将处理相同的数据结构(双的2D数组),但这里没有关于输入类型的
1.如何通过参数化构造函数设置这些属性?? 类文件 > 我想通过传递参数构造函数的setter设置这些属性,但它没有设置这些属性 在这里,我想通过toString方法显示属性 }对象文件 为什么我无法通过构造函数设置这些属性?? public class Runnable{public static void main(String[]args){Product p1=新产品(“pc”,“Grey
问题内容: 我有两个类,和,扩展了前一个类。 具有以下构造函数: 我将注意到所有实例变量都已设置为private。 同时,具有以下构造函数: 但是,这为我的构造函数引发了“找不到符号”错误。 我尝试使用,但是我的超类的私有范围阻止了这种情况。 我发现向我的构造函数中添加字段并允许我调用超级构造函数,但是我想知道是否存在一种无需在子类构造函数中传递其所有参数的情况下调用超级构造函数的方法? 问题答案
问题内容: 我在一个Activity中有一个IntentService,当我尝试调用该服务时,它将引发此错误,我发现这很奇怪,因为如果我声明了空的构造函数。 错误: AndroidManifest.xml 活动: 问题答案: 你是一个内部阶级。如果要将其保留在内部,请将其更改为static: 您可能需要阅读不同类型的嵌套类。google的第一个链接:http : //docs.oracle.com
问题内容: 到目前为止,我已经看到了两种在Java中设置变量值的方法。有时使用带有参数的构造函数,而其他setter方法则用于设置每个变量的值。 我知道,一旦使用“新”关键字实例化了一个类,构造函数就会在类内部初始化一个实例变量。 但是,什么时候使用构造函数,何时使用setter? 问题答案: 当您要创建对象的新实例时,应使用构造函数方法,该实例的值已填充(准备使用的对象中已填充值)。这样,您无需