JSF技术问题:如何在PhaseListener中修改输入字段值?
问题
我尝试在JSF的APPLY_REQUEST_VALUES阶段使用PhaseListener实现全局输入过滤清理,不用给每个字段单独加代码。第一步测试给空字段设值时,发现无法修改请求参数,抛出异常。
以下是测试用的PhaseListener代码,原本想先验证能否修改指定字段值,后续再扩展为遍历所有字段做清理:
public class SanitizeRequestValuesPhaseListener implements PhaseListener { private static final long serialVersionUID = 1L; public static final String SEARCH_FORM_INPUT_PRICE_TO = "searchForm:inputPriceTo"; public PhaseId getPhaseId() { return PhaseId.APPLY_REQUEST_VALUES; } public void afterPhase(final PhaseEvent event) { } public void beforePhase(final PhaseEvent event) { final FacesContext facesContext = event.getFacesContext(); final ExternalContext externalContext = facesContext.getExternalContext(); System.out.println(); final Map<String, String> requestParameters = externalContext.getRequestParameterMap(); printMap(requestParameters, "Request Parameters"); if (requestParameters.containsKey(SEARCH_FORM_INPUT_PRICE_TO)) { final String priceTo = requestParameters.get(SEARCH_FORM_INPUT_PRICE_TO); if (priceTo != null) { // just out of curiosity System.out.println(priceTo.getClass().getName()); } try { requestParameters.put(SEARCH_FORM_INPUT_PRICE_TO, "120"); } catch (final Throwable ex) { ex.printStackTrace(); } } } private static void printMap(final Map<?, ?> reqAttrs, final String title) { final StringBuilder sb; sb = new StringBuilder(); for (final Object key : reqAttrs.keySet()) { sb.append("* ("); sb.append(key); sb.append("="); sb.append(reqAttrs.get(key)); sb.append(")\n"); } System.out.println(title); System.out.println(sb); } }
想请教有没有其他方法可以修改/覆盖用户输入?或者有没有更合适的全局处理方案?核心需求是一个集中入口处理所有输入字段。
解决方案
方案1:用ServletRequestWrapper包装请求(推荐)
JSF获取的请求参数Map是不可修改的,因为底层HttpServletRequest的参数Map本身不支持修改。可以通过Servlet Filter包装请求,提前修改参数,让JSF后续拿到的就是处理后的值。
示例代码:
public class SanitizeRequestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化逻辑 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 包装请求,自定义参数获取逻辑 ServletRequest wrappedRequest = new SanitizedRequest(httpRequest); chain.doFilter(wrappedRequest, response); } @Override public void destroy() { // 销毁逻辑 } private static class SanitizedRequest extends HttpServletRequestWrapper { private Map<String, String[]> modifiedParams; public SanitizedRequest(HttpServletRequest request) { super(request); // 复制原参数并修改 modifiedParams = new HashMap<>(request.getParameterMap()); // 遍历所有参数做清理,比如处理空值、过滤特殊字符 for (Map.Entry<String, String[]> entry : modifiedParams.entrySet()) { String[] values = entry.getValue(); for (int i = 0; i < values.length; i++) { // 示例:空值替换为默认值,去除首尾空格 if (values[i] == null || values[i].trim().isEmpty()) { values[i] = "0"; } else { values[i] = values[i].trim(); } } entry.setValue(values); } // 单独处理特定字段 if (modifiedParams.containsKey("searchForm:inputPriceTo")) { modifiedParams.put("searchForm:inputPriceTo", new String[]{"120"}); } } @Override public String getParameter(String name) { String[] values = modifiedParams.get(name); return values != null && values.length > 0 ? values[0] : null; } @Override public String[] getParameterValues(String name) { return modifiedParams.get(name); } @Override public Map<String, String[]> getParameterMap() { return Collections.unmodifiableMap(modifiedParams); } } }
在web.xml中注册Filter:
<filter> <filter-name>sanitizeRequestFilter</filter-name> <filter-class>com.yourpackage.SanitizeRequestFilter</filter-class> </filter> <filter-mapping> <filter-name>sanitizeRequestFilter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping>
方案2:在PhaseListener中直接操作UI组件
APPLY_REQUEST_VALUES阶段,UI组件已接收请求参数并存储在submittedValue中,可遍历所有UIInput组件,修改其submittedValue来调整输入值,无需修改请求参数Map。
修改后的PhaseListener示例:
public class SanitizeRequestValuesPhaseListener implements PhaseListener { private static final long serialVersionUID = 1L; @Override public PhaseId getPhaseId() { return PhaseId.APPLY_REQUEST_VALUES; } @Override public void beforePhase(PhaseEvent event) { FacesContext facesContext = event.getFacesContext(); UIViewRoot viewRoot = facesContext.getViewRoot(); // 递归遍历所有UIInput组件 traverseComponents(viewRoot); } @Override public void afterPhase(PhaseEvent event) {} private void traverseComponents(UIComponent component) { for (UIComponent child : component.getChildren()) { if (child instanceof UIInput) { UIInput input = (UIInput) child; Object submittedValue = input.getSubmittedValue(); if (submittedValue instanceof String) { String value = (String) submittedValue; // 清理逻辑:去除空格,空值替换为默认值 String cleanedValue = value.trim(); if (cleanedValue.isEmpty()) { cleanedValue = "0"; } // 特殊字段单独处理 if ("searchForm:inputPriceTo".equals(input.getClientId())) { cleanedValue = "120"; } input.setSubmittedValue(cleanedValue); } } // 递归处理子组件 traverseComponents(child); } } }
在faces-config.xml中注册PhaseListener:
<lifecycle> <phase-listener>com.yourpackage.SanitizeRequestValuesPhaseListener</phase-listener> </lifecycle>
方案3:全局注册通用Converter
如果清理逻辑偏向值转换或格式化,可自定义通用Converter并全局注册,让所有UIInput组件默认使用。
示例Converter:
@FacesConverter(value = "globalSanitizeConverter", managed = true) public class GlobalSanitizeConverter implements Converter { @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null) { return null; } // 清理逻辑:去除空格,空值处理,简单XSS过滤 String cleanedValue = value.trim(); if (cleanedValue.isEmpty()) { // 根据组件类型返回对应默认值 if (component instanceof HtmlInputText && "number".equals(((HtmlInputText) component).getType())) { return 0; } return ""; } cleanedValue = cleanedValue.replaceAll("<", "<").replaceAll(">", ">"); return cleanedValue; } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { if (value == null) { return ""; } return value.toString(); } }
在faces-config.xml中设置为默认Converter:
<application> <default-converter>globalSanitizeConverter</default-converter> </application>
内容的提问来源于stack exchange,提问作者user9964388
相关产品推荐
相关产品推荐

