java - 将(自定义)解码器添加到 WebMVC 端点
问题描述
我有一个 WebMVC 端点:
@RequestMapping(path = "/execution/{id}", method = RequestMethod.POST)
public ResponseEntity<...> execute(@PathVariable String id) {
...
}
在这里,提供的id
应该首先被解码。是否可以定义一个“在后台”执行此操作的注释;也就是说,在调用端点之前?类似的东西:
@RequestMapping(path = "/execution/{id}", method = RequestMethod.POST)
public ResponseEntity<...> execute(@PathVariable @DecodedIdentifier String id) {
...
}
注意@DecodedIdentifier
注释。我知道它不存在,但它希望能解释我的意图。我知道这可以通过 Jersey 的 JAX-RS 实现来实现,但是 Spring 的 WebMVC 呢?
在这里,我使用的是 base64 解码,但我想知道是否也可以注入自定义解码器。
解决方案
尽管您可以使用注释,但我建议您Converter
为此目的使用自定义。
按照你的例子,你可以做这样的事情。
首先,您需要定义一个适合转换的自定义类。例如:
public class DecodedIdentifier {
private final String id;
public DecodedIdentifier(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
然后,Converter
为您的自定义类定义一个。它可以执行 Base64 解码:
public class DecodedIdentifierConverter implements Converter<String, DecodedIdentifier> {
@Override
public DecodedIdentifier convert(String source) {
return new DecodedIdentifier(Base64.getDecoder().decode(source));
}
}
为了告诉 Spring 这个转换器你有几个选择。
如果您正在运行 Spring Boot,您所要做的就是将类注释为 a@Component
并且自动配置逻辑将负责Converter
注册。
@Component
public class DecodedIdentifierConverter implements Converter<String, DecodedIdentifier> {
@Override
public DecodedIdentifier convert(String source) {
return new DecodedIdentifier(Base64.getDecoder().decode(source));
}
}
请务必配置您的组件扫描,以便 Spring 可以检测到@Component
类中的注释。
如果您在没有 Spring Boot 的情况下使用 Spring MVC,则需要“手动”注册Converter
:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DecodedIdentifierConverter());
}
}
注册后Converter
,您可以在您的Controller
:
@RequestMapping(path = "/execution/{id}", method = RequestMethod.POST)
public ResponseEntity<...> execute(@PathVariable DecodedIdentifier id) {
...
}
您还可以遵循其他选项。请考虑阅读这篇文章,它将为您提供有关该问题的更多信息。
作为旁注,上述文章表明您可以直接valueOf
在类中定义一个方法,该方法将存储转换服务的结果,DecodedIdentifier
在您的示例中,它可以让您摆脱Converter
该类:老实说,我从来没有尝试过这种方法,我不知道它在什么条件下可以工作。话虽如此,如果它有效,它可以简化您的代码。请,如果您认为合适,请尝试一下。
更新
感谢@Aman 评论,我仔细查看了 Spring 文档。之后,我发现,尽管我认为上述转换方法更适合用例——您实际上是在执行转换——另一种可能的解决方案可能是使用自定义Formatter
.
我已经知道 Spring 使用这种机制来执行多次转换,但我不知道可以基于 annotation 注册自定义格式化程序,这是答案中提出的原始想法。考虑像这样的注释DateTimeFormat
,这很有意义。事实上,这种方法之前已经在 Stackoverflow 中描述过(请参阅此问题中接受的答案)。
在您的情况下(基本上是上述针对您的情况的答案的转录):
首先,定义您的DecodedIdentifier
注释:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface DecodedIdentifier {
}
事实上,您可以考虑通过包括例如处理信息的编码来丰富注释。
然后,创建相应的AnnotationFormatterFactory
:
import java.text.ParseException;
import java.util.Base64;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.springframework.context.support.EmbeddedValueResolutionSupport;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.stereotype.Component;
@Component
public class DecodedIdentifierFormatterFactory extends EmbeddedValueResolutionSupport
implements AnnotationFormatterFactory<DecodedIdentifier> {
@Override
public Set<Class<?>> getFieldTypes() {
return Collections.singleton(String.class);
}
@Override
public Printer<?> getPrinter(DecodedIdentifier annotation, Class<?> fieldType) {
return this.getFormatter(annotation);
}
@Override
public Parser<?> getParser(DecodedIdentifier annotation, Class<?> fieldType) {
return this.getFormatter(annotation);
}
private Formatter getFormatter(DecodedIdentifier annotation) {
return new Formatter<String>() {
@Override
public String parse(String text, Locale locale) throws ParseException {
// If the annotation could provide some information about the
// encoding to be used, this logic will be highly reusable
return new String(Base64.getDecoder().decode(text));
}
@Override
public String print(String object, Locale locale) {
return object;
}
};
}
}
在 Spring MVC 配置中注册工厂:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new DecodedIdentifierFormatterFactory());
}
}
Controller
最后,完全按照您在问题中指出的那样使用您的 s 中的注释:
@RequestMapping(path = "/execution/{id}", method = RequestMethod.POST)
public ResponseEntity<...> execute(@PathVariable @DecodedIdentifier String id) {
...
}
推荐阅读
- uwp - StorageFolder.GetFilesAsync() 在 HoloLens 2 (ARM64) 中不起作用
- ios - 使用 scrollViewReader 时 SwiftUI ScrollView 滚动太远
- java - Javafx webview在运行时不显示破折号应用程序
- python - 无法使用列表理解访问分类报告指标
- php - 只有当变量满足特定条件时,有没有办法在 php 中重定向?
- javascript - 为位于 Blazor 子菜单中的 DOM 元素调用 JS 互操作函数
- python - 多处理时打开文件过多错误
- openlayers - 功能很小
- asp.net-mvc - ASP.NET Core 中每个用户的控制器
- python - 遍历 df 行并附加到没有名称和 dtype 的列表