How to programmatically send POST request to JSF page without using HTML form?

23,257

I understand that you're basically asking how to submit a JSF form programmatically using some HTTP client such as java.net.URLConnection or Apache HttpComponents Client, right?

You need to send a GET request first and make sure that you maintain the same HTTP session (basically, the JSESSIONID cookie) across requests. Let your HTTP client extract the Set-Cookie header from the response of the first GET request, obtain the JSESSIONID cookie from it and send it back as Cookie header of subsequent POST requests. This will maintain the HTTP session in the server side, otherwise JSF will treat it as a "View Expired" which may return either on a decently configured JSF web application a HTTP 500 error page with ViewExpiredException, or on a badly configured JSF web application behave as a page refresh.

As part of JSF's stateful nature and implied CSRF attack prevention, the forms must be submitted with a valid javax.faces.ViewState value as the client has retrieved itself on the initial GET request. You also need to make sure that you send the name=value pair of all other hidden fields and particularly the one of the submit button along as well.

So, if your initial GET request gives you this HTML back

<form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
    <input type="hidden" name="sampleForm" value="sampleForm" />
    <input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
    <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
</form>

then you need to parse it (Jsoup may be helpful in this) and extract the following request parameters:

  • sampleForm=sampleForm
  • sampleForm:sampleButton=ok
  • javax.faces.ViewState=j_id65

Finally send a POST request on /pages/main/main.smnet with exactly those request parameters (and the JSESSIONID cookie!). Be careful though, it's possible that a (poor) JSF developer has skipped e.g. id="sampleButton" from the <h:commandButton> and then JSF would autogenerate one which looks like in this format sampleForm:j_id42. You can't hardcode them as the value may change depending on the component's position in the server side tree and you would then really need to parse it out the obtained HTML.

Nonetheless, it's wise to contact the site owner/admin and ask if there isn't a web service API available for the task you had in mind. A decent Java EE website which uses a JSF application for a HTML frontend usually also uses a separate JAX-RS application for a REST frontend. It is much more easy and reliable to extract information via such a web service API than by scraping a HTML document.

See also:

Share:
23,257
pWoz
Author by

pWoz

JavaEE developer

Updated on July 09, 2022

Comments

  • pWoz
    pWoz over 1 year

    I have very simple JSF bean like shown below:

    import org.jboss.seam.annotations.Name;
    
    @Name(Sample.NAME)
    public class Sample {
    
        public static final String NAME="df";
    
        private String text = "text-test";
    
        public void sampleM(){
            System.out.println("Test: "+text);
        }
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    }
    

    And JSF form connected with this component:

    <h:form id="sampleForm">
            <h:commandButton id="sampleButton" action="#{df.sampleM()}" value="ok" />
    </h:form>
    

    Now, I would like to programmatically send POST request to this form.

    According to my investigation the key here are POST parameters. Selected properly gives proper results (String 'Test: text-test' is printed on serwer's console).

    So the question is: How should I select POST data that was correct?

    JSF form shown above produces this HTML form:

    <form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
        <input type="hidden" name="sampleForm" value="sampleForm" />
        <input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
        <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
    </form>
    

    So these parameters are corrent.

    But how can I find out what parameters (name and value) will be sufficient for any other component?

    For example: when I send POST data the same like in shown HTML form but with different 'javax.faces.ViewState' parameter value, component method will not be executed.

  • pWoz
    pWoz over 11 years
    Basically I would like to have access to JSF components and JSF life cycle without using HTML forms and HTML pages. Is it possible? Let's assume that the JSESSIONID is known. Can I ask server for valid javax.faces.ViewState value somehow? Maybe there is another way to get access to JSF components?
  • BalusC
    BalusC over 11 years
    No. JSF is a webbased MVC framework which is targeted on generating HTML form based applications, it is not a webservice framework. If that JSF website is under your full control, then you're basically using the wrong tool for the job. Create a webservice instead (you can even run it side by side with JSF). The Java EE API offers JAX-RS (RESTful) and JAX-WS (SOAP) APIs for this. If that JSF website is not under your full control, then contact the admin with question if there isn't a webservice available.
  • pWoz
    pWoz over 11 years
    So there is no way to generate such a scenario: I have a JSF application and several web services (all on the same server in one application). When the request arrives to the websevice method this method uses object stored in JSF application scope (there is no need to query database) ?
  • BalusC
    BalusC over 11 years
    The JSF application scope is under the covers just represented by attributes of ServletContext. If the webservice runs in the same webapp and container, it surely has access to the same ServletContext instance and thus also all of its attributes.
  • GingerHead
    GingerHead almost 10 years
    Please @balusC, can you answer this question stackoverflow.com/questions/23503948/…
  • Zyl
    Zyl over 9 years
    We successfully are abusing JSF as a webservice by communicating serialized Java objects to the server through HTTP GET parameters and retrieve a serialized response-object in the page content through an outputText component. It isn't even that hacky. We did not use JAX WS because it imposes constraints on how our database has to look like for authentication (Realms) and we'd have ended up depending on another JEE mechanism (Roles) which we don't have the resources to study.