当我测试一个控制器时,我发现下面的代码可以正常工作。但我不明白为什么模型在映射后返回后包含“PasspProperty”属性。PasspProperty类仅使用验证约束进行注释。在控制器中,它只在方法中使用@Valid annotation进行注释,如下面的代码所示。
我在控制器中有以下映射:
获取:
@GetMapping("/accounts/change-password")
public ModelAndView changePass(Principal principal) {
ModelAndView mv = new ModelAndView("change-password");
mv.addObject("passProperties", new PassProperties());
return mv;
}
帖子:
@PostMapping("/accounts/change-password")
public ModelAndView changePass(@Valid PassProperties passProperties, BindingResult bindingResult, Principal principal) {
ModelAndView mv = new ModelAndView("change-password");
if (bindingResult.hasErrors() == false) {
User user = userService.findUser(principal.getName());
String oldPass = passProperties.getOldPass();
if (encoder.matches(oldPass, user.getPassword())) {
user.setPassword(encoder.encode(passProperties.getNewPass1()));
userService.saveUser(user);
mv.addObject("msg", "Hasło zostało zmienione");
} else {
mv.addObject("msg", "Stare hasło nie pasuje.");
}
}
return mv;
}
我添加了以下测试和输出。如您所见,返回的ModelAndView包含未添加到@PostMapping方法中的PasspProperty。PasspProperty对象仅用@Valid注释,PasspProperty类中的字段用约束@NotNull、@Length和@NotBlank注释。它不会在任何地方用@ModelAttribute之类的东西进行注释。测试:
@Test
void changePassPost_whenPassPropertiesHasNoErrors_returnsModelWithPassProperties_And_ChangesPassword() throws Exception {
User principal = new User ("user", "user", true, "LVL99");
PassProperties passProperties = new PassProperties();
passProperties.setOldPass("user");
passProperties.setNewPass1("zaq1");
passProperties.setNewPass2("zaq1");
when(userService.findUser("user")).thenReturn(principal);
when(encoder.encode(passProperties.getNewPass1())).thenReturn(passProperties.getNewPass1());
when(encoder.matches(anyString(),anyString())).thenReturn(true);
ModelAndView mv = mockMvc.perform(MockMvcRequestBuilders.post("/accounts/change-password")
.with(SecurityMockMvcRequestPostProcessors.csrf())
.with(SecurityMockMvcRequestPostProcessors.user(new UserDetailsImpl(principal)))
.param("oldPass", passProperties.getOldPass())
.param("newPass1", passProperties.getNewPass1())
.param("newPass2", passProperties.getNewPass2())
)
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.model().attributeExists("passProperties"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.view().name("change-password"))
.andReturn().getModelAndView();
System.out.println("-------------------------------");
System.out.println(mv.getModel().get("passProperties"));
System.out.println(mv.getModel().get("msg"));
System.out.println("-------------------------------");
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
verify(encoder, times(1)).encode(passProperties.getNewPass1());
verify(userService, times(1)).saveUser(userCaptor.capture());
assertEquals(passProperties.getNewPass1(), userCaptor.getValue().getPassword());
assertEquals(passProperties.getNewPass1(), principal.getPassword());
}
输出:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /accounts/change-password
Parameters = {oldPass=[user], newPass1=[zaq1], newPass2=[zaq1], _csrf=[8fc305ee-f0eb-42a6-8588-cce3ffdba469]}
Headers = []
Body = null
Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a37a47df: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a37a47df: Principal: com.example.JabaVeans.service.UserDetailsImpl@58e46572; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: LVL99}
Handler:
Type = com.example.JabaVeans.controller.AccountsController
Method = com.example.JabaVeans.controller.AccountsController#changePass(PassProperties, BindingResult, Principal)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = change-password
View = null
Attribute = passProperties
value = PassProperties(oldPass=user, newPass1=zaq1, newPass2=zaq1)
errors = []
Attribute = msg
value = Hasło zostało zmienione
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Language:"en", Content-Type:"text/html;charset=UTF-8", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = text/html;charset=UTF-8
Body = <!DOCTYPE html>
Forwarded URL = null
Redirected URL = null
Cookies = []
-------------------------------
PassProperties(oldPass=user, newPass1=zaq1, newPass2=zaq1)
Hasło zostało zmienione
-------------------------------
2020-08-22 18:01:49.655 INFO 12964 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-08-22 18:01:49.665 INFO 12964 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Process finished with exit code 0
我知道,我可以覆盖"PassProperties"与新的PassProperties在@Post映射擦除以前的值,但我真的很困惑为什么它会发生。
如果您检查PassProperties类,也许它有一些验证注释,如@NotNull、@Min、@Max、@Email、@size等。使用控制器中的@Valid注释,Spring将根据您在类中定义的验证注释检查PassProperties对象是否有效。如果PassProperties对象的某些字段无效,该方法将不会启动,Spring将返回一个基于其中的错误。
我相信PassProperties有一些与密码相关的属性,比如oldPass、newPass。基于你的代码,@GetMap的方法Change ePass只是返回一个新的PassProperties类对象实例。ChangePass的@Post映射正在接收一个PassProperties对象,可能是由某个用户发送的,在这种情况下,就像我上面解释的那样,只有当所有验证都是真的(因为@Valid注释)时才会工作。
假设我们有一个使用Spring MVC和Spring Security配置的APIendpoint。我们希望能够处理@RequestMapping和@Secured注释对,其中仅有的@Secured注释值因对而异。这样,我们就可以根据相同请求的安全规则返回不同的响应主体。 这可以避免在方法体中直接检查安全规则,从而使代码更易于维护。 有一个不起作用的例子,下面是我们想要做的: 我们如何才能做到这一
我在我的SpringMVC项目中得到了这个错误。我必须在浏览器中手动添加完整的路径来运行代码,但我不能直接运行它。 web.xml:
http://some-amazon-link:8080/the-war-name/login 现在我正在纠结如何在Spring MVC项目中添加“/the-war-name/”以及在哪里添加“/the-war-name/”。 它在 null
我有一个登录控制器,我将“/login”路径映射到两个不同的方法。一个用于get,另一个用于post。 我有百里香叶的形状 输入数据并单击提交时,将调用带有 GET 请求的方法。我通过在两种方法中插入断点来了解这一点。此外,网址的末尾现在有一个 ?错误。我还像这样将第二种方法的 url 映射更改为“doLogin” 并将形式改为这样 它有效。我可以输入数据并点击提交按钮,我在doLogin方法中。
我正在用Android开发一个报警应用程序。流程非常简单,我只是创建了一个pendingent,然后在AlarmManager中调用setExact()方法,如下所示。 经过一些测试,我意识到,使用上面的代码片段,我只能设置一个警报,因为我将PendingContent的requestCode设置为零,如果我将另一个警报的requestCode设置为0,那么它将覆盖前一个警报。有没有办法在不更改请
我需要将POJO转换成地图。我尝试使用对象映射器,但是像timestamp这样的类型在最终的映射中要么以字符串的形式出现,要么以长的形式出现。有没有什么工具可以做一个简单的转换,使映射具有与POJO中完全相同的对象?(我知道我可以使用反射,但想看看是否有更简单的方法。)