From 5e318cb7f4d967c3c25a57dce6dd724447647e0c Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 24 Mar 2026 09:18:13 +0800 Subject: [PATCH] feat: finish bilingual session auth learning lab --- .../com/demo/action/DashboardAction.java | 44 ++ .../com/demo/action/FileUploadAction.java | 4 +- .../classes/com/demo/action/LoginAction.java | 33 +- .../classes/com/demo/action/LogoutAction.java | 26 + .../classes/com/demo/action/UserAction.java | 14 +- .../com/demo/action/ValidationAction.java | 16 +- .../action/interceptor/AuthInterceptor.java | 19 + web/WEB-INF/classes/struts.xml | 68 +- web/WEB-INF/views/index.jsp | 601 ++++++++++++++++++ web/{ => WEB-INF/views}/upload/index.jsp | 68 +- web/{ => WEB-INF/views}/upload/success.jsp | 24 +- web/WEB-INF/views/user/dashboard.jsp | 341 ++++++++++ web/WEB-INF/views/user/form.jsp | 126 ++++ web/WEB-INF/views/user/login.jsp | 274 ++++++++ web/{ => WEB-INF/views}/user/success.jsp | 79 ++- web/WEB-INF/views/validation/form.jsp | 133 ++++ .../views}/validation/success.jsp | 41 +- web/assets/struts-lab.js | 92 +++ web/demo/ajax/index.jsp | 4 +- web/demo/hello/index.jsp | 2 +- web/demo/model/index.jsp | 30 +- web/demo/upload/index.jsp | 24 +- web/hello.jsp | 24 +- web/index.jsp | 516 +-------------- web/user/form.jsp | 102 --- web/user/login.jsp | 176 ----- web/validation/form.jsp | 109 ---- 27 files changed, 1911 insertions(+), 1079 deletions(-) create mode 100644 web/WEB-INF/classes/com/demo/action/DashboardAction.java create mode 100644 web/WEB-INF/classes/com/demo/action/LogoutAction.java create mode 100644 web/WEB-INF/classes/com/demo/action/interceptor/AuthInterceptor.java create mode 100644 web/WEB-INF/views/index.jsp rename web/{ => WEB-INF/views}/upload/index.jsp (57%) rename web/{ => WEB-INF/views}/upload/success.jsp (78%) create mode 100644 web/WEB-INF/views/user/dashboard.jsp create mode 100644 web/WEB-INF/views/user/form.jsp create mode 100644 web/WEB-INF/views/user/login.jsp rename web/{ => WEB-INF/views}/user/success.jsp (56%) create mode 100644 web/WEB-INF/views/validation/form.jsp rename web/{ => WEB-INF/views}/validation/success.jsp (60%) create mode 100644 web/assets/struts-lab.js delete mode 100644 web/user/form.jsp delete mode 100644 web/user/login.jsp delete mode 100644 web/validation/form.jsp diff --git a/web/WEB-INF/classes/com/demo/action/DashboardAction.java b/web/WEB-INF/classes/com/demo/action/DashboardAction.java new file mode 100644 index 0000000..d4100aa --- /dev/null +++ b/web/WEB-INF/classes/com/demo/action/DashboardAction.java @@ -0,0 +1,44 @@ +package com.demo.action; + +import com.opensymphony.xwork2.ActionSupport; +import org.apache.struts2.interceptor.SessionAware; + +import java.util.Map; + +public class DashboardAction extends ActionSupport implements SessionAware { + + private Map session; + private String displayName; + private String role; + private String loginTime; + + @Override + public String execute() { + displayName = value(LoginAction.SESSION_USER, "ops-admin"); + role = value(LoginAction.SESSION_ROLE, "admin"); + loginTime = value(LoginAction.SESSION_LOGIN_TIME, "--"); + return SUCCESS; + } + + private String value(String key, String fallback) { + Object value = session == null ? null : session.get(key); + return value == null ? fallback : String.valueOf(value); + } + + public String getDisplayName() { + return displayName; + } + + public String getRole() { + return role; + } + + public String getLoginTime() { + return loginTime; + } + + @Override + public void setSession(Map session) { + this.session = session; + } +} diff --git a/web/WEB-INF/classes/com/demo/action/FileUploadAction.java b/web/WEB-INF/classes/com/demo/action/FileUploadAction.java index 130fd46..0876f60 100644 --- a/web/WEB-INF/classes/com/demo/action/FileUploadAction.java +++ b/web/WEB-INF/classes/com/demo/action/FileUploadAction.java @@ -27,11 +27,11 @@ public class FileUploadAction extends ActionSupport { } if (fileCount == 0) { - addActionError("Select at least one file before submitting the demo."); + addActionError("请至少选择一个文件再提交。 / Select at least one file before submitting the demo."); return INPUT; } - summary = "This demo captures upload metadata only. It does not persist files to disk, which keeps the sample safe for classroom use."; + summary = "metadata-only"; return SUCCESS; } diff --git a/web/WEB-INF/classes/com/demo/action/LoginAction.java b/web/WEB-INF/classes/com/demo/action/LoginAction.java index 701dc9f..75a003a 100644 --- a/web/WEB-INF/classes/com/demo/action/LoginAction.java +++ b/web/WEB-INF/classes/com/demo/action/LoginAction.java @@ -1,20 +1,25 @@ package com.demo.action; import com.opensymphony.xwork2.ActionSupport; +import org.apache.struts2.interceptor.SessionAware; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Map; -public class LoginAction extends ActionSupport { +public class LoginAction extends ActionSupport implements SessionAware { + + public static final String SESSION_USER = "demoUser"; + public static final String SESSION_ROLE = "demoRole"; + public static final String SESSION_LOGIN_TIME = "demoLoginTime"; private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private Map session; private String username; private String password; private String displayName; - private String role; private String loginTime; - private String recommendation; @Override public String execute() { @@ -26,14 +31,15 @@ public class LoginAction extends ActionSupport { } if ("admin".equals(username) && "123456".equals(password)) { - displayName = "System Demo Admin"; - role = "Administrator"; + displayName = "ops-admin"; loginTime = LocalDateTime.now().format(TIME_FORMATTER); - recommendation = "Continue with the user form, validation sample, or upload flow to explore the rest of the demo."; + session.put(SESSION_USER, displayName); + session.put(SESSION_ROLE, "admin"); + session.put(SESSION_LOGIN_TIME, loginTime); return SUCCESS; } - addActionError("Invalid demo credentials. Use admin / 123456."); + addActionError("演示账号不正确,请使用 admin / 123456。 / Invalid demo credentials. Use admin / 123456."); return INPUT; } @@ -43,10 +49,10 @@ public class LoginAction extends ActionSupport { return; } if (username == null || username.length() < 3) { - addFieldError("username", "Username must be at least 3 characters."); + addFieldError("username", "用户名至少 3 个字符。 / Username must be at least 3 characters."); } if (password == null || password.length() < 6) { - addFieldError("password", "Password must be at least 6 characters."); + addFieldError("password", "密码至少 6 个字符。 / Password must be at least 6 characters."); } } @@ -79,15 +85,12 @@ public class LoginAction extends ActionSupport { return displayName; } - public String getRole() { - return role; - } - public String getLoginTime() { return loginTime; } - public String getRecommendation() { - return recommendation; + @Override + public void setSession(Map session) { + this.session = session; } } diff --git a/web/WEB-INF/classes/com/demo/action/LogoutAction.java b/web/WEB-INF/classes/com/demo/action/LogoutAction.java new file mode 100644 index 0000000..b6c184e --- /dev/null +++ b/web/WEB-INF/classes/com/demo/action/LogoutAction.java @@ -0,0 +1,26 @@ +package com.demo.action; + +import com.opensymphony.xwork2.ActionSupport; +import org.apache.struts2.interceptor.SessionAware; + +import java.util.Map; + +public class LogoutAction extends ActionSupport implements SessionAware { + + private Map session; + + @Override + public String execute() { + if (session != null) { + session.remove(LoginAction.SESSION_USER); + session.remove(LoginAction.SESSION_ROLE); + session.remove(LoginAction.SESSION_LOGIN_TIME); + } + return SUCCESS; + } + + @Override + public void setSession(Map session) { + this.session = session; + } +} diff --git a/web/WEB-INF/classes/com/demo/action/UserAction.java b/web/WEB-INF/classes/com/demo/action/UserAction.java index 0a11bee..ae9d7a6 100644 --- a/web/WEB-INF/classes/com/demo/action/UserAction.java +++ b/web/WEB-INF/classes/com/demo/action/UserAction.java @@ -14,6 +14,7 @@ public class UserAction extends ActionSupport { private String phone; private String submittedAt; private String profileStage; + private boolean profileReady; public String submit() { username = normalize(username); @@ -25,22 +26,23 @@ public class UserAction extends ActionSupport { } submittedAt = LocalDateTime.now().format(TIME_FORMATTER); - profileStage = (phone != null && phone.length() >= 7) ? "Profile ready for follow-up demos." : "Profile captured. Add a stronger phone number next time."; + profileReady = phone != null && phone.length() >= 7; + profileStage = profileReady ? "ready" : "review"; return SUCCESS; } private boolean isValid() { boolean valid = true; if (username == null || username.length() < 3) { - addFieldError("username", "Username must be at least 3 characters."); + addFieldError("username", "用户名至少 3 个字符。 / Username must be at least 3 characters."); valid = false; } if (email == null || !email.contains("@")) { - addFieldError("email", "Enter a valid email address."); + addFieldError("email", "请输入有效邮箱。 / Enter a valid email address."); valid = false; } if (phone == null || phone.replaceAll("[^0-9]", "").length() < 7) { - addFieldError("phone", "Enter at least 7 digits for the phone number."); + addFieldError("phone", "手机号至少 7 位数字。 / Enter at least 7 digits for the phone number."); valid = false; } return valid; @@ -81,4 +83,8 @@ public class UserAction extends ActionSupport { public String getProfileStage() { return profileStage; } + + public boolean isProfileReady() { + return profileReady; + } } diff --git a/web/WEB-INF/classes/com/demo/action/ValidationAction.java b/web/WEB-INF/classes/com/demo/action/ValidationAction.java index 5823814..661a7af 100644 --- a/web/WEB-INF/classes/com/demo/action/ValidationAction.java +++ b/web/WEB-INF/classes/com/demo/action/ValidationAction.java @@ -15,13 +15,15 @@ public class ValidationAction extends ActionSupport { private String bio; private String scoreBand; private String submittedAt; + private boolean seniorTrack; @Override public String execute() { username = normalize(username); email = normalize(email); bio = normalize(bio); - scoreBand = age >= 30 ? "Mid-career operator profile" : "Early-career operator profile"; + seniorTrack = age >= 30; + scoreBand = seniorTrack ? "mid" : "early"; submittedAt = LocalDateTime.now().format(TIME_FORMATTER); return SUCCESS; } @@ -29,16 +31,16 @@ public class ValidationAction extends ActionSupport { @Override public void validate() { if (username == null || username.trim().length() < 3 || username.trim().length() > 20) { - addFieldError("username", "Username must be between 3 and 20 characters."); + addFieldError("username", "用户名长度需在 3 到 20 之间。 / Username must be between 3 and 20 characters."); } if (email == null || !email.contains("@") || email.indexOf('@') == email.length() - 1) { - addFieldError("email", "Enter a valid email address."); + addFieldError("email", "请输入有效邮箱。 / Enter a valid email address."); } if (age == null || age < 18 || age > 60) { - addFieldError("age", "Age must be between 18 and 60."); + addFieldError("age", "年龄需在 18 到 60 之间。 / Age must be between 18 and 60."); } if (bio != null && bio.trim().length() > 240) { - addFieldError("bio", "Bio must stay under 240 characters."); + addFieldError("bio", "简介不能超过 240 个字符。 / Bio must stay under 240 characters."); } } @@ -85,4 +87,8 @@ public class ValidationAction extends ActionSupport { public String getSubmittedAt() { return submittedAt; } + + public boolean isSeniorTrack() { + return seniorTrack; + } } diff --git a/web/WEB-INF/classes/com/demo/action/interceptor/AuthInterceptor.java b/web/WEB-INF/classes/com/demo/action/interceptor/AuthInterceptor.java new file mode 100644 index 0000000..f8cec8a --- /dev/null +++ b/web/WEB-INF/classes/com/demo/action/interceptor/AuthInterceptor.java @@ -0,0 +1,19 @@ +package com.demo.action.interceptor; + +import com.demo.action.LoginAction; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.interceptor.AbstractInterceptor; + +import java.util.Map; + +public class AuthInterceptor extends AbstractInterceptor { + + @Override + public String intercept(ActionInvocation invocation) throws Exception { + Map session = invocation.getInvocationContext().getSession(); + if (session != null && session.get(LoginAction.SESSION_USER) != null) { + return invocation.invoke(); + } + return "login"; + } +} diff --git a/web/WEB-INF/classes/struts.xml b/web/WEB-INF/classes/struts.xml index 538cb35..ed7416f 100644 --- a/web/WEB-INF/classes/struts.xml +++ b/web/WEB-INF/classes/struts.xml @@ -4,13 +4,30 @@ "http://struts.apache.org/dtds/struts-2.5.dtd"> - + + + + + + + + + + + + + + loginPage + - - /index.jsp + /WEB-INF/views/index.jsp + + + + /WEB-INF/views/user/login.jsp @@ -18,18 +35,44 @@ - /user/success.jsp - /user/login.jsp + dashboard + /WEB-INF/views/user/login.jsp + + + + + /WEB-INF/views/user/dashboard.jsp + + + + loginPage + + + + + /WEB-INF/views/user/form.jsp + + + + + /WEB-INF/views/validation/form.jsp + + + + + /WEB-INF/views/upload/index.jsp - /user/success.jsp - /user/form.jsp + + /WEB-INF/views/user/success.jsp + /WEB-INF/views/user/form.jsp - /upload/success.jsp - /upload/index.jsp + + /WEB-INF/views/upload/success.jsp + /WEB-INF/views/upload/index.jsp @@ -37,12 +80,13 @@ - /validation/success.jsp - /validation/form.jsp + + /WEB-INF/views/validation/success.jsp + /WEB-INF/views/validation/form.jsp - + diff --git a/web/WEB-INF/views/index.jsp b/web/WEB-INF/views/index.jsp new file mode 100644 index 0000000..43c7831 --- /dev/null +++ b/web/WEB-INF/views/index.jsp @@ -0,0 +1,601 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + + + + + + Struts2 学习实验台 + + + +
+
+
+
+
Struts2 学习实验台
+

把零散示例整理成一条可讲解、可演示、可验证的学习路线

+

这个入口页现在不再只是样例链接集合,而是把经典 Struts2 的动作映射、参数绑定、Session 登录、表单校验和文件上传串成一个可循序学习的 Demo 门户。

+
+ +
+
+
关键动作8
+
鉴权节点4
+
表单实验3
+
JSON 示例2
+
+
+ +
+ + +
+
+
实验目录
+

核心实验入口

+
+
+
+ 动作 + 基础 +
+

Hello Action

+

运行最小 Struts2 动作,观察请求参数如何进入 Action,再由结果页返回浏览器。

+ +
+ +
+
+ 登录 + Session +
+

登录与仪表盘

+

这部分是本项目的核心改造点:用经典 Session 登录和拦截器串起后续实验页。

+ +
+ +
+
+ 表单 + 绑定 +
+

用户资料提交

+

演示字段绑定、错误回显和成功汇总页,也是最适合讲参数注入的例子。

+ +
+ +
+
+ 校验 + 输入规则 +
+

字段校验实验

+

对比校验失败和成功页面,理解为什么 Struts2 会在业务逻辑之前先跑 validate。

+ +
+ +
+
+ 上传 + 安全演示 +
+

文件上传元数据

+

保留 multipart 绑定教学价值,但不真正落盘,适合本地和 VPS 环境安全演示。

+ +
+ +
+
+ JSON + 接口风格 +
+

AJAX 与 REST 风格返回

+

保留 JSON 动作和 REST 风格示例,用来说明经典 MVC 项目如何逐步演进到接口输出。

+ +
+
+ +
+
+
+
+ + + + + diff --git a/web/upload/index.jsp b/web/WEB-INF/views/upload/index.jsp similarity index 57% rename from web/upload/index.jsp rename to web/WEB-INF/views/upload/index.jsp index 351c101..a1bd93b 100644 --- a/web/upload/index.jsp +++ b/web/WEB-INF/views/upload/index.jsp @@ -1,11 +1,11 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> - + - Upload Demo - Struts2 Demo Lab + 上传元数据实验 - Struts2 学习实验台 + + +
+
+
+
+
登录后仪表盘
+

Session 已建立,后续实验页现在通过拦截器受保护

+

这一页的作用不是展示“登录成功”四个字,而是把你接下来的学习路线展开:继续看用户表单、校验、上传,或者回到门户解释这条 Session 鉴权链路是如何工作的。

+
+ +
+ +
+
+ 当前用户 + +
+
+ 当前角色 + +
+
+ 登录时间 + +
+
+
+ +
+
受保护实验
+

现在你可以进入这些需要登录的页面

+
+
+
+ 字段绑定 + 受保护 +
+

用户资料表单

+

继续看请求参数如何绑定到 Action 属性,并在成功页生成一份汇总结果。

+ +
+ +
+
+ validate() + 受保护 +
+

字段校验页

+

对比校验失败和成功状态,理解为什么 Struts2 会在业务逻辑前先执行校验方法。

+ +
+ +
+
+ multipart + 受保护 +
+

上传元数据页

+

这里保留文件上传的教学价值,但不把文件真正写入磁盘,适合安全演示。

+ +
+ +
+
+ 解释链路 + 回看入口 +
+

回门户讲完整链路

+

回到入口页,可以从公开入口、登录动作、Session 写入、拦截器保护这条路线整体复盘。

+ +
+
+
+ +
+
鉴权流程
+

这次你实际走过的 Struts2 登录链路

+
+
+ 1. 表单提交到 login Action +

用户名和密码先进入 Action,校验失败会直接回到登录页。

+
+
+ 2. Action 写入 Session +

账号通过后,用户、角色和登录时间被放进 Session。

+
+
+ 3. 拦截器保护页面动作 +

用户表单、校验页、上传页和仪表盘都先经过 AuthInterceptor。

+
+
+ 4. 未登录会被打回登录页 +

如果没有 Session,安全动作不会继续执行,而是直接跳回登录入口。

+
+
+
+
+ + + + + diff --git a/web/WEB-INF/views/user/form.jsp b/web/WEB-INF/views/user/form.jsp new file mode 100644 index 0000000..70c8dbe --- /dev/null +++ b/web/WEB-INF/views/user/form.jsp @@ -0,0 +1,126 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + + + + + + 用户资料表单 - Struts2 学习实验台 + + + +
+
受保护表单
+

通过 Action 属性绑定提交一份用户资料

+

这个页面现在放在登录保护链路里。重点不是字段本身,而是观察请求参数如何进入 Action,再在成功页汇总成一份结构化结果。

+ + +
+ 当前未登录 +

这个实验页已经接入 Session 保护。请先登录,再回来看字段绑定和错误回显。

+
+ +
+ +
+ 建议观察点 +

依次观察:字段名如何对应 Action 属性、校验错误如何回显、成功页如何读取 Action 结果。

+
+ + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + +
+ + +
+
+ + diff --git a/web/WEB-INF/views/user/login.jsp b/web/WEB-INF/views/user/login.jsp new file mode 100644 index 0000000..ba05e9d --- /dev/null +++ b/web/WEB-INF/views/user/login.jsp @@ -0,0 +1,274 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + + + + + + Session 登录实验 - Struts2 学习实验台 + + + +
+
+
+
Session 登录章节
+ +
+

用最经典的 Struts2 Session 登录,把后续实验页保护起来

+

这个页面不只是演示登录表单,而是把 Action 校验、写入 Session、拦截器校验和登录后仪表盘串成一个完整章节,适合你系统掌握老项目最常见的鉴权思路。

+ +
+ 演示账号 +

用户名:admin
密码:123456

+
+ +
+
+ 这一页讲什么 + Action 校验、错误回显、Session 写入、登录后跳转。 +
+
+ 下一步看什么 + 登录成功后先看仪表盘,再进入用户表单、校验页和上传页。 +
+
+ + +
+ +
+
登录表单
+

输入演示账号

+ + +
+ 当前已登录 +

你已经拥有可访问的 Session,可以直接进入仪表盘或退出后重新体验登录过程。

+
+ +
+ + +
+
+ + +
+ + +
+
+ +
+ + +
+
+ + +
+
+
+
+ + + + + diff --git a/web/user/success.jsp b/web/WEB-INF/views/user/success.jsp similarity index 56% rename from web/user/success.jsp rename to web/WEB-INF/views/user/success.jsp index 1306abd..51a73ec 100644 --- a/web/user/success.jsp +++ b/web/WEB-INF/views/user/success.jsp @@ -1,11 +1,11 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> - + - Success Dashboard - Struts2 Demo Lab + 用户资料汇总 - Struts2 学习实验台 + + +
+
受保护校验页
+

在业务执行前,先把输入规则讲清楚

+

这个实验页专门用来讲 Struts2 的 validate()。你可以故意输错,再看字段错误如何被带回到原页面。

+ + +
+ 当前未登录 +

校验页也已经接入 Session 保护。请先登录,再体验校验失败和成功两种状态。

+
+ +
+ +
+ 建议观察点 +

先故意输入一个短用户名、错误邮箱和超范围年龄,再修正后重新提交,对比两次页面反馈。

+
+ + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + +
+ + +
+
+ + diff --git a/web/validation/success.jsp b/web/WEB-INF/views/validation/success.jsp similarity index 60% rename from web/validation/success.jsp rename to web/WEB-INF/views/validation/success.jsp index 8bac204..a9c3fca 100644 --- a/web/validation/success.jsp +++ b/web/WEB-INF/views/validation/success.jsp @@ -1,11 +1,11 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> - + - Validation Summary - Struts2 Demo Lab + 校验结果汇总 - Struts2 学习实验台 - - -
-
-
-
-
Struts2 Demo Lab
-

Turn scattered examples into a guided demo portal

-

This workspace now acts like a mini learning application instead of a pile of sample pages. You can jump into action basics, login flow, validation, uploads, and JSON demos from one place.

-
- -
-
-
Live action routes5
-
Guide pages4
-
Demo forms3
-
JSON endpoints2
-
Core lab track4
-
Completed labs0
-
-
- -
- - -
-
-
Request lifecycle
-

How to explain Struts2 with one mental model

-
-
- 1. Request enters -

The browser sends a URL and optional form fields or query params.

-
-
- 2. Action mapping -

struts.xml resolves the action name and target class.

-
-
- 3. Parameter binding -

Struts populates action properties before execute() runs.

-
-
- 4. Result routing -

The returned result name decides which JSP or JSON renderer will respond.

-
-
-
- -
-
Catalog
-

Interactive demos and guides

-
-
-
- Action - Beginner -
-

Hello action

-

Run a real Struts action, inject a request parameter, and inspect the rendered result page.

- -
- -
-
- Form - Core flow -
-

Login flow

-

Use demo credentials, see field validation, and land on a richer post-login dashboard.

- -
- -
-
- Action - Form submit -
-

User intake form

-

Submit a small profile payload and see action-backed validation with a success summary.

- -
- -
-
- Validation - Teaching -
-

Validation demo

-

Test length, email, age, and text limits while keeping the page readable for explanation.

- -
- -
-
- Upload - Safe demo -
-

Upload metadata demo

-

Capture file metadata through Struts form binding without writing anything to disk.

- -
- -
-
- JSON - API style -
-

AJAX and REST payloads

-

Show how Struts2 can return JSON from actions for AJAX examples and lightweight REST-style demos.

- -
- -
-
- Guide - Patterns -
-

Model binding guide

-

Compare property-driven and model-driven input binding to explain how Struts builds action state.

- -
-
- -
- -
-
Learning pipeline
-

Suggested order for the full demo

-
-
- 1. Start with hello -

See the smallest action and result mapping first.

-
-
- 2. Move to forms -

Login and user intake show parameter binding in action.

-
-
- 3. Validate inputs -

Explain field errors, business rules, and success pages.

-
-
- 4. Expand to JSON -

Close the session with AJAX and REST-style payload demos.

-
-
-
-
-
-
- - - - +<% + response.sendRedirect(request.getContextPath() + "/index.action"); +%> diff --git a/web/user/form.jsp b/web/user/form.jsp deleted file mode 100644 index e140ae0..0000000 --- a/web/user/form.jsp +++ /dev/null @@ -1,102 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ taglib prefix="s" uri="/struts-tags" %> - - - - - - User Form - Struts2 Demo Lab - - - -
-
User action
-

Capture a user profile through Struts binding

-

This form now demonstrates a more realistic user intake flow with field errors and a structured success screen.

- - -
- - -
-
- -
- - -
-
- -
- - -
-
- - -
- - -
- - diff --git a/web/user/login.jsp b/web/user/login.jsp deleted file mode 100644 index 678831b..0000000 --- a/web/user/login.jsp +++ /dev/null @@ -1,176 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ taglib prefix="s" uri="/struts-tags" %> - - - - - - Login Demo - Struts2 Demo Lab - - - -
-
-
Demo login
-

Login flow with validation and guided next steps

-

This page turns the original login example into a better teaching demo. It keeps validation close to the form, shows a reusable credentials note, and links to follow-up demos after success.

- -
- Demo credentials -

Username: admin
Password: 123456

-
- -
-
- What this demonstrates - Action execution, field validation, action errors, and result routing. -
-
- Suggested next route - After login, continue with the user form or validation demo. -
-
- - -
- -
-
Sign in
-

Enter the demo account

- - -
-
- - -
- - -
-
- -
- - -
-
- - -
-
-
- - diff --git a/web/validation/form.jsp b/web/validation/form.jsp deleted file mode 100644 index 2091838..0000000 --- a/web/validation/form.jsp +++ /dev/null @@ -1,109 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ taglib prefix="s" uri="/struts-tags" %> - - - - - - Validation Demo - Struts2 Demo Lab - - - -
-
Validation demo
-

Test field validation with realistic profile inputs

-

This form shows how Struts2 can keep validation close to the action while still rendering a friendlier teaching page.

- - -
- - -
-
- -
- - -
-
- -
- - -
-
- -
- - -
-
- - -
- - -
- -