使用HTTP解码器进行表单验证
在这个例子中,我们将创建一个异步Servlet,将联系人添加到列表中,解析请求并在 Decoder
的帮助下处理表单 验证。 把这个例子看作是MVC模式的一个简明代表。
- 为了建立一个
Contact
的模型,我们将创建一个带有字段(姓名、年龄、地址)、构造器和字段访问器的普通Java类。 - 为了简化例子,我们将使用一个
ArrayList
来存储Contact
对象。ContactDAO
接口和它的实现被用于此目的。 - 为了建立一个视图,我们将使用一个单一的HTML文件,在Mustache模板引擎的帮助下进行编译。
- 一个
AsyncServlet
将被用作控制器。 我们还将添加RoutingServlet
,用于将一个具体的请求路由到一个特定的端点。 - Decoder 为你提供解析请求的工具。
这里我们将只考虑 HttpDecoderExample
类与 AsyncServlet
,因为它包含了ActiveJ的特定功能。
创建 HttpDecoderExample
类
让我们创建 HttpDecoderExample
类,它扩展了 HttpServerLauncher
。 通过扩展 HttpServerLauncher
,我们将照顾到 服务器的生命周期和服务管理。 接下来,我们提供两个基于HTTP的自定义解析器 解码器- ADDRESS_DECODER
和 CONTACT_DECODER
- 这将被用于验证。
public final class HttpDecoderExample extends HttpServerLauncher { private static final String SEPARATOR = "-";
private static final Decoder<Address> ADDRESS_DECODER = Decoder. of(Address::new, ofPost("title", "") .validate(param -> !param.isEmpty(), "Title cannot be empty") );
private static final Decoder<Contact> CONTACT_DECODER = Decoder.of(Contact::new, ofPost("name") .validate(name -> !name.isEmpty(), "Name cannot be empty"), ofPost("age") .map(Mapper.ofEx(Integer::valueOf, "Cannot decode age")) .validate(age -> age >= 18, "Age must not be less than 18"), ADDRESS_DECODER.withId("contact-address") );
note
如果你想了解更多关于模板引擎集成的信息,请查看 这个例子
另外,我们需要创建 applyTemplate(Mustache mustache, Map<String, Object> scopes) 方法,将提供的 Mustache模板填入给定的数据。
private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) { ByteBufWriter writer = new ByteBufWriter(); mustache.execute(writer, scopes); return writer.getBuf();}
接下来,让我们提供一个 ContactDAOImpl 工厂方法。
@ProvidesContactDAO dao() { return new ContactDAOImpl();}
现在我们有了创建 AsyncServlet
来处理请求所需的一切。
@ProvidesAsyncServlet mainServlet(Reactor reactor, ContactDAO contactDAO) { Mustache contactListView = new DefaultMustacheFactory().compile("static/contactList.html"); return RoutingServlet.builder(reactor) .with("/", request -> HttpResponse.ok200() .withBody(applyTemplate(contactListView, Map.of("contacts", contactDAO.list()))) .toPromise()) .with(POST, "/add", request -> request.loadBody() .then($ -> { //[START REGION_3] Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request); //[END REGION_3] if (decodedUser.isLeft()) { contactDAO.add(decodedUser.getLeft()); } Map<String, Object> scopes = new HashMap<>(); scopes.put("contacts", contactDAO.list()); if (decodedUser.isRight()) { //noinspection ConstantConditions - is 'right', hence not 'null' scopes.put("errors", decodedUser.getRight().toMap(SEPARATOR)); } return HttpResponse.ok200() .withBody(applyTemplate(contactListView, scopes)) .toPromise(); })) .build();}
- 这里我们提供了一个
AsyncServlet
,它从客户端接收HttpRequests
,根据路由路径创建HttpResponses
,并发送。 - 在
RoutingServlet
内,定义了两条路由路径。 第一个匹配到根路由的请求"/"
- 它只是显示一个联系人列表。 第二个,"/add"
- 是一个HTTPPOST
方法,用于添加或拒绝添加新用户。 我们将在上述HTTP解码器
的帮助下,通过使用decode(request)
方法处理这个请求解析。
@ProvidesAsyncServlet mainServlet(Reactor reactor, ContactDAO contactDAO) { Mustache contactListView = new DefaultMustacheFactory().compile("static/contactList.html"); return RoutingServlet.builder(reactor) .with("/", request -> HttpResponse.ok200() .withBody(applyTemplate(contactListView, Map.of("contacts", contactDAO.list()))) .toPromise()) .with(POST, "/add", request -> request.loadBody() .then($ -> { //[START REGION_3] Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request); //[END REGION_3] if (decodedUser.isLeft()) { contactDAO.add(decodedUser.getLeft()); } Map<String, Object> scopes = new HashMap<>(); scopes.put("contacts", contactDAO.list()); if (decodedUser.isRight()) { //noinspection ConstantConditions - is 'right', hence not 'null' scopes.put("errors", decodedUser.getRight().toMap(SEPARATOR)); } return HttpResponse.ok200() .withBody(applyTemplate(contactListView, scopes)) .toPromise(); })) .build();}
- 无论是 代表两个可能的数据类型的值(
联系
,DecodeErrors
)。 ** 要么是 左(联系
) 要么是 右(DecodeErrors**) 。 为了确定一个解析是否成功,我们通过使用isLeft()
和isRight()
方法来检查它的值。 最后,写下main()
方法,它将启动我们的应用程序。
public static void main(String[] args) throws Exception { Launcher launcher = new HttpDecoderExample(); launcher.launch(args);}
运行应用程序
如果你想运行这个例子,你需要从GitHub上 克隆ActiveJ ,然后 将其导入Maven项目。 查阅分支机构 v6.0-beta2。 在运行这个例子之前,构建项目(Ctrl F9 for IntelliJ IDEA)。
然后打开 HttpDecoderExample
类,该类位于 activej/examples/tutorials/decoder
并运行其 main()
方法。 打开你喜欢的浏览器,进入 localhost:8080