在spring boot应用中,我们通常使用@controller注解标记控制器类,并通过@getmapping、@postmapping等注解来定义http请求与处理方法之间的映射关系。这些注解使得开发者能够清晰地将特定的url路径绑定到对应的业务逻辑。
例如,以下PostController定义了处理/posts/new路径的GET和POST请求的方法:
@Controller
public class PostController {
@Autowired
private PostService postService;
// ... 其他方法 ...
@GetMapping("/posts/new")
public String createNewPost(Model model){
// 调用服务层逻辑准备数据并返回视图名称
return postService.createNewPost(model);
}
@PostMapping("/posts/new")
public String saveNewPost(@ModelAttribute Post post){
// 处理表单提交,保存数据并重定向
postService.save(post);
return "redirect:/posts/" + post.getId();
}
}从上述代码看,/posts/new路径的GET和POST请求都已明确映射到createNewPost和saveNewPost方法。然而,即使映射看起来完全正确,客户端仍可能收到404错误。这通常不是因为Spring无法找到控制器方法,而是因为控制器方法内部的逻辑导致了404的返回。
当Spring MVC成功将请求路由到控制器方法后,该方法的执行结果将决定最终的响应。如果控制器方法返回一个视图名称,Spring会尝试解析并渲染该视图。但如果业务逻辑判断某种条件不满足,例如所需数据不存在,它可能会显式地返回一个表示“未找到”的视图名称,从而导致应用层面的404。
在上述博客应用场景中,@GetMapping("/posts/new")方法调用了postService.createNewPost(model)。让我们深入查看PostService中的实现:
@Service
public class PostService {
@Autowired
private AccountService accountService;;
// ... 其他方法 ...
public String createNewPost(Model model){
// 尝试通过硬编码的邮箱查找账户
Optional optionalAccount = accountService.findByEmail("[email protected]");
if(o
ptionalAccount.isPresent()){
// 如果找到账户,则创建新帖子对象并添加到模型
Post post = new Post();
post.setAccount(optionalAccount.get());
model.addAttribute("post", post);
// 返回“post_new”视图
return "post_new";
}else{
// 如果未找到账户,则直接返回“404”字符串
return "404";
}
}
}问题根源分析:
从createNewPost方法中可以看出,它尝试通过一个硬编码的邮箱地址[email protected]去查找一个Account。如果accountService.findByEmail()方法返回的Optional为空(即没有找到对应的账户),那么该方法会直接返回字符串"404"。
当Spring MVC接收到"404"这个字符串作为视图名称时,它会尝试去寻找名为404.html(或其他配置的视图文件)的模板并渲染。如果该模板不存在,或者即便存在,但用户期望的是一个功能页面而非错误页面,这都会导致用户体验上的问题,并且在某些情况下可能被误认为是URL映射错误。
解决方案:
在Thymeleaf模板中,th:action属性用于定义表单提交的目标URL。Spring EL表达式@{...}用于生成URL。
原始的post_new.html中使用了: