Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does <h:form> renderer add a hidden field with form ID as name and value

I was reading the JSF implementation <h:form> rendering. To my surprise, I see that (in Mojarra, MyFaces + Tomahawk), they add a hidden input field on an encodeBegin() method.

Here is the sample code for FormRenderer in Mojarra:

@Override
public void encodeBegin(FacesContext context, UIComponent component)
      throws IOException {

    rendererParamsNotNull(context, component);

    if (!shouldEncode(component)) {
        return;
    }

    ResponseWriter writer = context.getResponseWriter();
    assert(writer != null);
    String clientId = component.getClientId(context);
    // since method and action are rendered here they are not added
    // to the pass through attributes in Util class.
    writer.write('\n');
    writer.startElement("form", component);
    writer.writeAttribute("id", clientId, "clientId");
    writer.writeAttribute("name", clientId, "name");
    writer.writeAttribute("method", "post", null);
    writer.writeAttribute("action", getActionStr(context), null);
    String styleClass =
          (String) component.getAttributes().get("styleClass");
    if (styleClass != null) {
        writer.writeAttribute("class", styleClass, "styleClass");
    }
    String acceptcharset = (String)
          component.getAttributes().get("acceptcharset");
    if (acceptcharset != null) {
        writer.writeAttribute("accept-charset", acceptcharset,
                              "acceptcharset");
    }

    RenderKitUtils.renderPassThruAttributes(context,
                                            writer,
                                            component,
                                            ATTRIBUTES);
    writer.writeText("\n", component, null);

    // this hidden field will be checked in the decode method to
    // determine if this form has been submitted.         
    writer.startElement("input", component);
    writer.writeAttribute("type", "hidden", "type");
    writer.writeAttribute("name", clientId,
                          "clientId");
    writer.writeAttribute("value", clientId, "value");
    writer.endElement("input");
    writer.write('\n');

    // Write out special hhidden field for partial submits
    String viewId = context.getViewRoot().getViewId();
    String actionURL =
        context.getApplication().getViewHandler().getActionURL(context, viewId);
    ExternalContext externalContext = context.getExternalContext();
    String encodedActionURL = externalContext.encodeActionURL(actionURL);
    String encodedPartialActionURL = externalContext.encodePartialActionURL(actionURL);
    if (encodedPartialActionURL != null) {
        if (!encodedPartialActionURL.equals(encodedActionURL)) {
            writer.startElement("input", component);
            writer.writeAttribute("type", "hidden", "type");
            writer.writeAttribute("name", "javax.faces.encodedURL", null);
            writer.writeAttribute("value", encodedPartialActionURL, "value");
            writer.endElement("input");
            writer.write('\n');
        }
    }

    if (!writeStateAtEnd) {
        context.getApplication().getViewHandler().writeState(context);
        writer.write('\n');
    }
}

My Questions:

  1. Why is there a hidden input field that gets assigned the id component.getClientId(context), i.e., a UIForm component? What's the purpose of the hidden field?
  2. In Mojarra, you don't have the option to assign your own id attribute on <h:form> but you can on MyFaces. How does JSF treat UIForm in each cases (e.g. when MyFaces has its an explicit provided id)?
  3. Mojarra form enctype is defaulted to application/x-www-form-urlencoded. Can it support multipart/form-data or text/plain?

Thanks

like image 802
Buhake Sindi Avatar asked Feb 16 '23 23:02

Buhake Sindi


1 Answers

Why is there a hidden input field that gets assigned the id component.getClientId(context), i.e., a UIForm component? What's the purpose of the hidden field?

Look closer at the comment (line numbers are from Mojarra 2.1.19):

153        // this hidden field will be checked in the decode method to
154        // determine if this form has been submitted.         
155        writer.startElement("input", component);
156        writer.writeAttribute("type", "hidden", "type");
157        writer.writeAttribute("name", clientId,
158                              "clientId");
159        writer.writeAttribute("value", clientId, "value");
160        writer.endElement("input");
161        writer.write('\n');

This is thus been used to determine which form was been submitted. This determination happens in turn in the FormRenderer#decode() which is invoked when JSF needs to determine the HTTP request parameters during apply request values phase:

96         // Was our form the one that was submitted?  If so, we need to set
97         // the indicator accordingly..
98         Map<String, String> requestParameterMap = context.getExternalContext()
99               .getRequestParameterMap();
100        if (requestParameterMap.containsKey(clientId)) {
101            if (logger.isLoggable(Level.FINE)) {
102                logger.log(Level.FINE,
103                           "UIForm with client ID {0}, submitted",
104                           clientId);
105            }
106            ((UIForm) component).setSubmitted(true);
107        } else {
108            ((UIForm) component).setSubmitted(false);
109        }

In other words, it controls the outcome of UIForm#isSubmitted() property. It may be useful in the following use cases involving multiple forms in a page:

  • Check which form has an error
  • How can we use multiple <h:messages> tags or <h:message> tags in a single JSF page?

In Mojarra, you don't have the option to assign your own id attribute on <h:form> but you can on MyFaces. How does JSF treat UIForm in each cases (e.g. when MyFaces has its an explicit provided id)?

I'm not sure what you're talking about. This,

<h:form id="formId">

works fine for me in Mojarra as well.


Mojarra form enctype is defaulted to application/x-www-form-urlencoded. Can it support multipart/form-data or text/plain?

This is as per JSF specification. And yes, you can just use enctype attribute the usual way.

<h:form enctype="multipart/form-data">

Whether the submit of such a form is properly recognized by JSF, is a second. Until JSF 2.2, JSF doesn't have builtin support for multipart/form-data. JSF component libraries solve this by supplying a servlet filter along with their file upload components, usually using Apache Commons FileUpload under the covers. JSF 2.2 just delegates straight to new Servlet 3.0 HttpServletRequest#getPart() API without the need for a servlet filter using a 3rd party API. See also among others How to upload files to server using JSP/Servlet?

like image 155
BalusC Avatar answered Feb 27 '23 14:02

BalusC