Eclipse Krazo(Jakarta MVC)的使用

news/2024/12/13 2:40:17/

文章目录

  • 背景
  • Jakarta MVC规范
  • Eclipse Krazo
  • 使用前的思考
  • 全局配置
  • Controller示例
    • 返回View的三种写法
    • View中用到的Model如何设值?
    • View中如何获取Model中的值?
  • 参数校验
  • 防止CSRF
    • Krazo是如何实现的呢?
      • 如何生成csrf的token?
      • 如何校验csrf呢?
      • 自定义失败页面
  • 其他用法以及配置
  • 备注

背景

在N年以前,我估计在零几年?对我等95后开发者来说,可能意味着上古时代?提起MVC那时候还是Struts的时代,后来随着Spring的出现,SpringMVC开始渐渐被越来越多的开发者使用。Struts走向没落,中间还出现Vraptor,后来Struts2发布,Struts的生命彻底走到尽头。因为Spring不是Jakarta EE的规范,所以对于Jakarta EE来说一直缺少MVC规范,终于在近几年,社区开始建立MVC规范。

目前MVC框架大概是三家,SpringMVC, Struts2, Eclipse Krazo(Jakarta MVC规范的实现)。

Jakarta MVC规范

最新的规范是2.1.规范内容非常少,不复杂。建议阅读一遍规范,基本就知道怎么用了。Jakarta MVC是建立在Jakarta Restful Web Services(jakarta.ws.rs)之上的,并支持CDI

来自官方规范1.4节
Most of the terminology used in this specification is borrowed from other specifications such as Jakarta RESTful Web Services and Jakarta Contexts and Dependency Injection

关于Jakarta Restful Web Services的实现RestEasy已经在RestEasy的入门与使用这篇文章里做过详细的介绍,这里不做过多展开。

Eclipse Krazo

krazo官方网站,文档入口如下图
在这里插入图片描述
文档内容同样不多,估计几分钟就看完了。在一开始的时候,可以和上面的规范文档一起看。

使用前的思考

有使用过MVC框架的同学对MVC整体的开发流程是很清楚的,基本一个典型的业务流程是如下:

  1. 访问A页面,A.jsp/A.xhtml(jsf)。请求相应的Controller里的方法accessPageA(),将A页面用到的model设置进去,然后在A页面使用EL表达式进行取值。
  2. 进入A页面,进行一些操作,提交表单。请求相应的Controller里的submit()方法,对参数进行校验,处理业务,根据不同的结果跳转到不同的页面。

下面的例子就以这两步为例,给出一些简单的示例代码

全局配置

@ApplicationPath("mvc")
public class MvcApplication extends Application {/*** 配置文件 https://eclipse-ee4j.github.io/krazo/documentation/latest/index.html#_properties_default_view_file_extension_org_eclipse_krazo_defaultviewfileextension* @return*/@Overridepublic Map<String, Object> getProperties() {final Map<String, Object> properties = new HashMap<>();// 设置页面(View)文件夹properties.put(ViewEngine.DEFAULT_VIEW_FOLDER, "/WEB-INF/views/");properties.put(Properties.DEFAULT_VIEW_FILE_EXTENSION, "jsp");// 设置form表单的method属性,允许除了get和post之外的其他请求properties.put(FormMethodOverwriter.FORM_METHOD_OVERWRITE, Options.ENABLED);properties.put(FormMethodOverwriter.HIDDEN_FIELD_NAME, FormMethodOverwriter.DEFAULT_HIDDEN_FIELD_NAME);return properties;}
}

Controller示例

主要注解就是@Controller, 注意这里我的注解是放在class上的,代表该类下所有的方法都是返回到页面(View)。如果你只需要某个方法返回页面,其他方法是Restful的,那么只需要把Controller注解放到相应的方法上即可。

@RequestScoped
@Controller
@Path("test")
public class MvcController {@Injectprivate Models models;/*** 获得校验结果*/@Injectprivate BindingResult bindingResult;@GET@Path("helloMvc/{path}")public Response helloMvc(@PathParam("path") String path) {models.put("message", "Hello MVC, " + path);return Response.ok("helloMvc.jsp").build();}@DELETE@Path("deleteMvc")public String deleteMvc(@FormParam("message") @MvcBinding @NotBlank String message) {if (bindingResult.isFailed()) {models.put("errors", bindingResult.getAllMessages());return "deleteMvcResult.jsp";}models.put("message", message);return "deleteMvcResult.jsp";}@POST@Path("csrf")@CsrfProtected@View("csrf.jsp")public void csrf() {models.put("message", "csrf");}
}

返回View的三种写法

如上述代码所示

  1. 通过Response对象设置页面路径
  2. 返回String,String内容是页面路径
  3. 使用@View注解,注解内容是页面路径

View中用到的Model如何设值?

This specification supports two kinds of models: the first is based on CDI @Named beans, and the second on the Models interface which defines a map between names and objects. Jakarta MVC provides view engines for Jakarta Server Pages and Facelets out of the box, which support both types.

官方文档提供了Model设值两种方式,这里示例代码使用Models这个接口,直接通过CDI注入进来。

View中如何获取Model中的值?

以JSP为例,可以使用EL表达式或者request.getAttribute()方法

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--jstl 3.0的uri已经改变了--%>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<html>
<head><title>helloMvc</title>
</head>
<body>
<div>这是返回给页面的信息:${message}<br>这是使用jstl标签库的信息:<c:out value="${message}"/><br>
<%--    https://jakarta.ee/specifications/mvc/2.1/jakarta-mvc-spec-2.1.html#view_engines--%>这是使用request.getAttribute()方法获取的信息:<%=request.getAttribute("message")%><br>
</div>
<div><form action="${pageContext.request.contextPath}/mvc/test/deleteMvc" method="POST"><input type="hidden" name="_method" value="DELETE"/><input type="text" name="message" value="${message}"/><input type="submit" value="提交"/></form>
</div>
<div><form action="${pageContext.request.contextPath}/mvc/test/csrf" method="post">
<%--       https://jakarta.ee/specifications/mvc/2.1/jakarta-mvc-spec-2.1.html#mvc_context --%><input type="hidden" name="${mvc.csrf.name}" value="${mvc.csrf.token}"/><input type="submit" value="测试csrf"/></form>
</div>
</body>
</html>

为什么request.getAttribute能够获取到model的值呢?这是因为规范要求,实现此规范时,需要将model通过setAttribute的方式设置进去
在这里插入图片描述
在Krazo源码ServletViewEngine中也可以看到这一步
在这里插入图片描述

参数校验

使用@MvcBinding注解.使用方法如上述代码所示,关于各种校验注解可以直接使用Jakarta Bean Validation 相关注解。如果想要了解更多可以参考Hibernate-Validator使用这篇博文。

当校验失败时,我们这里的处理方式是,把校验失败的信息放到errors中,并返回一个页面,在页面中展示。

防止CSRF

要集成防止CSRF攻击,官方也提供了具体的规范要求和实现。如上述代码所示,只要在表单里添加一个隐藏域,隐藏域的name和value是EL表达式,在进入JSP页面时,通过EL表达式获得token,和在后端方法上加上@CsrfProtected注解即可

Krazo是如何实现的呢?

如何生成csrf的token?

上述所述,

${mvc.csrf.name}和${mvc.csrf.token}

这两个EL表达式完成了name和value的生成.关键源码如下
入口点是CsrfProtectFilter
在这里插入图片描述
最终放到session里。

这里你可能会疑惑,为什么这里的EL表达式是mvc呢,那是因为
规范规定
在这里插入图片描述
规范实现
在这里插入图片描述

如何校验csrf呢?

入口点是CsrfValidateFilter
在这里插入图片描述
在这里插入图片描述
很简单,就是去session里拿一下,验证一下。如果校验不通过则抛出异常

自定义失败页面

虽然官方提供了一个默认的实现CsrfExceptionMapper,但是我们想CSRF校验失败时,自己控制跳到自己项目里的页面

@Provider
@Priority(Priorities.USER)
public class MvcExceptionHandler implements ExceptionMapper<CsrfValidationException> {@Contextprivate HttpServletResponse response;@Contextpublic HttpServletRequest request;@Contextprivate ServletContext servletContext;@SneakyThrows@Overridepublic Response toResponse(final CsrfValidationException e) {request.setAttribute("errors", e.getMessage());
//        两种写法都可以
//        servletContext.getRequestDispatcher("/WEB-INF/views/csrf.jsp").forward(request, response);request.getServletContext().getRequestDispatcher("/WEB-INF/views/csrf.jsp").forward(request, response);return null;}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<html>
<head><title>csrf</title>
</head>
<body>
<c:if test="${not empty errors}">csrf校验失败:<br><ul><c:forEach items="${errors}" var="error"><li>${error}</li></c:forEach></ul><br>
</c:if>
<c:if test="${not empty message}">${message} 校验成功
</c:if>
</body>
</html>

其他用法以及配置

关于本篇博文中没有提到的其他配置以及用法,可以参考Krazo官方文档 Configuration一节

备注

  • 示例代码仓库

http://www.ppmy.cn/news/437451.html

相关文章

牛客网最全的 Java 面试八股文大合集汇总

就目前大环境来看&#xff0c;跳槽成功的难度比往年高很多。一个明显的感受&#xff1a;今年的面试&#xff0c;无论一面还是二面&#xff0c;都很考验 Java 程序员的技术功底。这不马上又到了面试跳槽的黄金段&#xff0c;成功升职加薪&#xff0c;不成功饱受打击。当然也要注…

力扣动态规划专题(三)完全背包 518.零钱兑换II 377. 组合总和 Ⅳ 70. 爬楼梯 322. 零钱兑换 279.完全平方数 步骤及C++实现

文章目录 完全背包一维dp数组 滚动数组 518.零钱兑换II377. 组合总和 Ⅳ70. 爬楼梯322. 零钱兑换279.完全平方数 完全背包 完全背包的物品数量是无限的&#xff0c;01背包的物品数量只有一个 完全背包和01背包分许步骤一样&#xff0c;唯一不同就是体现在遍历顺序上 有n件物品…

华为手机连接Wi-Fi提示 “网络拒绝接入”

本方法适用于&#xff1a; 1、华为或其他拥有类似功能的手机。 什么功能后边讲 2、wifi路由器设置mac白名单 以上两点都满足&#xff0c;大部分都是我以下讲的情况。 功能&#xff1a;华为手机EMUI 8&#xff0e;0以上版本已经默认开启了MAC地址随机化功能 我这边的路由器设…

远程连接出现拒绝访问

排除防火墙的情况下有以上错误提示解决办法如下&#xff1a; 服务器 开始-运行-输入 services.msc&#xff0c;打开计算机的服务&#xff0c;找到 Remote Desktop Services&#xff0c;登陆&#xff0c;选择此账户&#xff0c;输入用户名“网络服务”&#xff08;注意是网络服务…

win10映射Samba服务器的网络驱动器,一直提示拒绝访问

账户和密码完全正确&#xff0c;但是就是显示拒绝访问&#xff0c;下面提供一种解决方法。 可能的原因&#xff1a;虽然在系统中通过useradd创建了目标用户&#xff0c;但是并没有将这个用户设定为samba的共享用户。解决方法sudo smbpasswd -a 用户名

拒绝连接

java.net.BindException: Address already in use: JVM_Bind 端口被占用&#xff0c;可以使用netstat -an 查看端口占用情况&#xff0c;关闭对应的进程或者tomcat换端口 java.net.ConnectException: Connection refused: connect ping一下服务端的IP&#xff0c;可能服务端…

一招搞定Intel(R) Wireless-AC 9560显示感叹号,无法打开wifi模块!

废话少说&#xff0c;直接上解决办法 解决方案&#xff1a; 第一步:进去设备管理器_网络适配器&#xff0c;卸载带感叹号的程序&#xff0c;不要勾选删除文件第二步:关机&#xff0c;拔掉电源鼠标等所有外设&#xff0c;等两分钟&#xff0c;释放静电第三步:长按开机键30秒&a…

win10移动热点拒绝接入

win10开了移动热点之后&#xff0c;发现手机连接不上。 手机端显示已停用&#xff08;网络故障&#xff09;。然后就纳闷了。 在电脑段查看热点适配器信息之后发现如下&#xff1a; 然后查看详细信息&#xff1a; 还不知道是哪里出了问题导致电脑的热点拒绝手机端接入的。 最后…