You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("<", "&lt;").replaceAll(">", "&gt;");
        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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 07:00:55