Spring 4中@ControllerAdvice的改进

在Spring 4的许多新功能中,我发现了@ControllerAdvice的改进。 @ControllerAdvice是@Component的特殊化,用于定义适用于所有@RequestMapping方法的@ ExceptionHandler,@ InitBinder和@ModelAttribute方法。 在Spring 4之前,@ ControllerAdvice在同一Dispatcher Servlet中协助了所有控制器。 在Spring 4中,它已经发生了变化。 从Spring 4开始,可以将@ControllerAdvice配置为支持定义的控制器子集,而仍可以使用默认行为。

@ControllerAdvice协助所有控制器

假设我们要创建一个错误处理程序,它将向用户显示应用程序错误。 我们假设这是一个基本的Spring MVC应用程序,其中Thymeleaf作为视图引擎,并且我们有一个ArticleController,它具有以下@RequestMapping方法:

package pl.codeleak.t.articles;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("article")
class ArticleController {

    @RequestMapping("{articleId}")
    String getArticle(@PathVariable Long articleId) {
        throw new IllegalArgumentException("Getting article problem.");
    }
}

如我们所见,我们的方法抛出了一个假想异常。 现在,使用@ControllerAdvice创建一个异常处理程序。 (这不仅是Spring中处理异常的可能方法)。

package pl.codeleak.t.support.web.error;

import com.google.common.base.Throwables;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
class ExceptionHandlerAdvice {

 @ExceptionHandler(value = Exception.class)
 public ModelAndView exception(Exception exception, WebRequest request) {
  ModelAndView modelAndView = new ModelAndView("error/general");
  modelAndView.addObject("errorMessage", Throwables.getRootCause(exception));
  return modelAndView;
 }
}

该课程不是公开的,不是公开的。 我们添加了@ExceptionHandler方法,该方法将处理所有类型的Exception,并将返回“错误/常规”视图:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Error page</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link href="../../../resources/css/bootstrap.min.css" rel="stylesheet" media="screen" th:href="@{/resources/css/bootstrap.min.css}"/>
    <link href="../../../resources/css/core.css" rel="stylesheet" media="screen" th:href="@{/resources/css/core.css}"/>
</head>
<body>
<div class="container" th:fragment="content">
    <div th:replace="fragments/alert :: alert (type='danger', message=${errorMessage})"> </div>
</div>
</body>
</html>

为了测试解决方案,我们可以运行服务器,或者(最好)使用Spring MVC Test模块创建一个测试。 由于我们使用Thymeleaf,因此可以验证渲染的视图:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RootConfig.class, WebMvcConfig.class})
@ActiveProfiles("test")
public class ErrorHandlingIntegrationTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void before() {
        this.mockMvc = webAppContextSetup(this.wac).build();
    }

    @Test
    public void shouldReturnErrorView() throws Exception {
        mockMvc.perform(get("/article/1"))
                .andDo(print())
                .andExpect(content().contentType("text/html;charset=ISO-8859-1"))
                .andExpect(content().string(containsString("java.lang.IllegalArgumentException: Getting article problem.")));
    }
}

我们期望内容类型为text / html,并且视图包含带有错误消息HTML片段。 但是,它并不是真正的用户友好型。 但是测试是绿色的。

使用上述解决方案,我们提供了一种处理所有控制器错误的通用机制。 如前所述,我们可以使用@ControllerAdvice:做更多的事情。 例如:

@ControllerAdvice
class Advice {

    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("attr1", "value1");
        model.addAttribute("attr2", "value2");
    }

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.setBindEmptyMultipartFiles(false);
    }
}

@ControllerAdvice协助选定的控制器子集

从Spring 4开始,可以通过批注(),basePackageClasses(),basePackages()方法来自定义@ControllerAdvice,以选择控制器的一个子集来提供帮助。 我将演示一个简单的案例,说明如何利用此新功能。

假设我们要添加一个API以通过JSON公开文章。 因此,我们可以定义一个新的控制器,如下所示:

@Controller
@RequestMapping("/api/article")
class ArticleApiController {

    @RequestMapping(value = "{articleId}", produces = "application/json")
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    Article getArticle(@PathVariable Long articleId) {
        throw new IllegalArgumentException("[API] Getting article problem.");
    }
}

我们的控制器不是很复杂。 如@ResponseBody批注所示,它返回Article作为响应正文。 当然,我们要处理异常。 而且我们不希望以text / html的形式返回错误,而是以application / json的形式返回错误。 然后创建一个测试:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RootConfig.class, WebMvcConfig.class})
@ActiveProfiles("test")
public class ErrorHandlingIntegrationTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void before() {
        this.mockMvc = webAppContextSetup(this.wac).build();
    }

    @Test
    public void shouldReturnErrorJson() throws Exception {
        mockMvc.perform(get("/api/article/1"))
                .andDo(print())
                .andExpect(status().isInternalServerError())
                .andExpect(content().contentType("application/json"))
                .andExpect(content().string(containsString("{\"errorMessage\":\"[API] Getting article problem.\"}")));
    }
}

测试是红色的。 我们能做些什么使其绿色? 我们需要提出另一个建议,这次仅针对我们的Api控制器。 为此,我们将使用@ControllerAdvice注解()选择器。 为此,我们需要创建一个客户或使用现有注释。 我们将使用@RestController预定义注释。 带有@RestController注释的控制器默认情况下采用@ResponseBody语义。 我们可以通过将@Controller替换为@RestController并从处理程序的方法中删除@ResponseBody来修改我们的控制器:

@RestController
@RequestMapping("/api/article")
class ArticleApiController {

    @RequestMapping(value = "{articleId}", produces = "application/json")
    @ResponseStatus(value = HttpStatus.OK)
    Article getArticle(@PathVariable Long articleId) {
        throw new IllegalArgumentException("[API] Getting article problem.");
    }
}

我们还需要创建另一个将返回ApiError(简单POJO)的建议:

@ControllerAdvice(annotations = RestController.class)
class ApiExceptionHandlerAdvice {

    /**
     * Handle exceptions thrown by handlers.
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ApiError exception(Exception exception, WebRequest request) {
        return new ApiError(Throwables.getRootCause(exception).getMessage());
    }
}

这次运行测试套件时,两个测试均为绿色,这意味着ExceptionHandlerAdvice辅助了“标准” ArticleController,而ApiExceptionHandlerAdvice辅助了ArticleApiController。

摘要

在以上场景中,我演示了如何轻松利用@ControllerAdvice批注的新配置功能,希望您像我一样喜欢所做的更改。

参考文献

参考: @ControllerAdvice在我们SpringJCG合作伙伴 Rafal Borowiec的Codeleak.pl博客上的改进

翻译自: https://www.javacodegeeks.com/2013/11/controlleradvice-improvements-in-spring-4.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值