David-SkyMesh
David-SkyMesh

Reputation: 5171

WWW::Mechanize - how to POST without affecting page stack and/or current HTML::Form object?

I'm using WWW::Mechanize to automate placing 'orders' in our supplier's portal (with permission). It's pretty straight-forward to do so by filling the relevant form fields and submiting as normal.

However, the portal has been built with a JavaScript-capable client in mind, and some short-cuts were taken as a result; the most significant short-cut they took is that as you progress through a "wizard" (series of forms) with normal POSTS, they require that you "deallocate" some resources server side for the "previous wizard step" by doing an AJAX POST. In pseudo-code:

GET page #1
fill the fields of page #1
POST (submit the form) --> redirects to page #2.
POST (ajax request to "/delete.do") 
fill the fields of page #2
POST (submit the form) --> redirects to page #3.
POST (ajax request to "/delete.do")
fill the fields of page #3.
...

What's the easiest approach to do those ajax request to "/delete.do" requests?

I've tried...

Appoach (A)

If I inject a new form (referencing /delete.do in the action) into the DOM and use submit then the mech object will no longer have the HTML::Form object built from the previous redirects to page #X step.

If I just use back() from that point, does that make another GET to the server? (or is it just using the prior values from the page stack?)

Approach (B)

If I just use the post() method inherited from LWP::UserAgent to send the POST to /delete.do I get a security error -- I guess it's not using the cookie jar that has been set up by WWW::Mechanize.

Is there some canonical way to make an 'out-of-band' POST that:


UDPATE: For anyone trying to replicate the solution suggested by gangabass, you actually need to:

(1) Subclass WWW::Mechanize, overriding update_html such that a new content can be injected into the HTML on demand.

This content would normally be parsed by HTML::Form::parse(). The override sub needs to alter the first non-self parameter $html before calling the original implementation and returning the result.

package WWW::Mechanize::Debug;
use base 'WWW::Mechanize';

sub update_html {
  my ($self,$html) = @_;

  $html = $WWW::Mechanize::Debug::html_updater->($html)
    if defined($WWW::Mechanize::Debug::html_updater);

  return $self->SUPER::update_html($html);
}

1; 

(2) In the main program, use WWW::Mechanize::Debug as per WWW::Mechanize

use WWW::Mechanize::Debug;
my $mech = WWW::Mechanize::Debug->new;

(3) Inject the HTML form which will need to be submit()ed.

{ 
  my $formHTML = qq| 
    <form action="/delete.do" method="POST" name="myform">
      <!-- some relevant hidden inputs go here in the real solution -->
    </form>
  |;

  local $WWW::Mechanize::html_updater = sub {
    my ($html) = @_;
    $html =~ s|</body>|$formHTML</body>|;
  };

  # Load the page containing the normal wizard step content.
  $mech->get($the_url); 

  # This should how have an extra form injected into it.                            
}

(4) In a new scope clone() the mechanize object, fill the form and submit it!

{ 
   my $other = $mech->clone;
   my $myform = $separate->form_name('my_form');
   $myform->field('foo' => 'bar'); # fill in the relevant fields to be posted
   $myform->submit;
}

(5) Continue using the original mechanize object as if that form submission had never occurred.

Upvotes: 1

Views: 803

Answers (1)

gangabass
gangabass

Reputation: 10666

You need to clone your Mech object and make POST from cloned version. Something like:

{
    my $mech = $mech->clone();
    $mech->post(....);
}

But of course it will be better to make sub for this.

Upvotes: 2

Related Questions