Spring MVC中@RequestParam的自定义转换器

时间:2017-10-13 11:33:16

标签: spring spring-mvc spring-boot

我得到一个加密的String作为Spring参数的参数给Spring休息控制器方法。

我希望在字符串到达​​方法之前根据一些注释(比如@Decrypt)解密字符串,如下所示

@RequestMapping(value = "/customer", method = RequestMethod.GET)
public String getAppointmentsForDay(@RequestParam("secret") @Decrypt String customerSecret) {
    System.out.println(customerSecret);  // Needs to be a decrypted value.
   ...
}

这个用例中的自定义Formatter是正确的方法吗?

或者我应该使用自定义HandlerMethodArgumentResolver

3 个答案:

答案 0 :(得分:7)

org.springframework.format.Formatter的自定义实现是此用例的有效方法。这就是Spring本身如何为日期,货币,数字样式等实现格式化程序。

步骤:

  1. 声明注释:Decrypt

    import java.lang.annotation.*;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
    public @interface Decrypt {
    
    }
    
  2. 声明使用新注释的AnnotationFormatterFactory

    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 java.text.ParseException;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Locale;
    import java.util.Set;
    
    public class DecryptAnnotationFormatterFactory extends EmbeddedValueResolutionSupport
            implements AnnotationFormatterFactory<Decrypt> {
    
        @Override
        public Set<Class<?>> getFieldTypes() {
            Set<Class<?>> fieldTypes = new HashSet<>();
            fieldTypes.add(String.class);
            return Collections.unmodifiableSet(fieldTypes);
        }
    
        @Override
        public Printer<String> getPrinter(Decrypt annotation, Class<?> fieldType) {
            return configureFormatterFrom(annotation);
        }
    
        @Override
        public Parser<String> getParser(Decrypt annotation, Class<?> fieldType) {
            return configureFormatterFrom(annotation);
        }
    
        private Formatter<String> configureFormatterFrom(Decrypt annotation) {
            // you could model something on the Decrypt annotation for use in the decryption call
            // in this example the 'decryption' call is stubbed, it just reverses the given String
            // presumaby you implementaion of this Formatter will be different e.g. it will invoke your encryption routine
            return new Formatter<String>() {
                @Override
                public String print(String object, Locale locale) {
                    return object;
                }
    
                @Override
                public String parse(String text, Locale locale) throws ParseException {
                    return new StringBuilder(text).reverse().toString();
                }
            };
        }
    }
    
  3. 使用您的网络背景注册此格式化工厂:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    @Configuration
    public class WebConfigurer extends WebMvcConfigurerAdapter {
        @Override
        public void addFormatters(FormatterRegistry registry) {
            super.addFormatters(registry);
            registry.addFormatterForFieldAnnotation(new DecryptAnnotationFormatterFactory());
        }
    }
    
  4. 就是这样。

  5. 完成上述操作后,@RequestParam符合@Decrypt条件的所有用法都将通过parse()中声明的DecryptAnnotationFormatterFactory方法传递,以便您实施解密就在那里打电话。

    为证明这一点,以下测试通过:

    @RunWith(SpringRunner.class)
    @WebMvcTest(controllers = YourController.class)
    public class YourControllerTest {
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void theSecretRequestParameterWillBeConverted() throws Exception {
            MvcResult mvcResult = mockMvc.perform(get("/customer?secret=abcdef")).andExpect(status().isOk()).andReturn();
    
            // the current implementation of the 'custom' endpoint returns the value if the secret request parameter and
            // the current decrypt implementation just reverses the given value ...
            assertThat(mvcResult.getResponse().getContentAsString(), is("fedcba"));
        }
    }
    

答案 1 :(得分:2)

HandlerMethodArgumentResolver在这方面是最好的。

  1. 创建注释:
  2. from numpy import *
    
    foo= dtype(object, [])
    x = array([[2., 3.], [3.]], dtype=foo)
    
    1. 创建自定义HandlerMethodArgumentResolver:
    2. @Target(ElementType.PARAMETER)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface Decrypt {
          String value();
      }
      
      1. 注册解析器:
      2. public class DecryptResolver implements HandlerMethodArgumentResolver {
        
            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                return parameter.getParameterAnnotation(Decrypt.class) != null;
            }
        
            @Override
            public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
                    WebDataBinderFactory binderFactory) throws Exception {
                Decrypt attr = parameter.getParameterAnnotation(Decrypt.class);
                String encrypted = webRequest.getParameter(attr.value());
                String decrypted = decrypt(encrypted);
        
                return decrypted;
            }
        
            private String decrypt(String encryptedString) {
                // Your decryption logic here
        
                return "decrypted - "+encryptedString;
            }
        }
        
        1. 瞧,你有解密的参数。请注意,您不再需要使用@RequestParam。
        2. @Configuration
          @EnableMvc // If you're not using Spring boot
          public class WebConfig extends WebMvcConfigurerAdapter {
          
              @Override
              public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
                    argumentResolvers.add(new DecryptResolver());
              }
          }
          

答案 2 :(得分:0)

您可以尝试在CharacterEncodingFilter文件中添加init-param encoding UTF-8 web.xml。查看this example

但是,如果它仍然不起作用,您可以通过添加以下参数以及init-param来强制编码。

<init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
</init-param>

让我知道它是否适合你。

相关问题