I am wondering if anyone can share with me an example of multipart/form-data that contains:
Some form parameters
Multiple files
EDIT: I am maintaining a similar, but more in-depth answer at: https://stackoverflow.com/a/28380690/895245
To see exactly what is happening, use nc -l or an ECHO server and a user agent like a browser or cURL.
Save the form to an .html file:
<form action="http://localhost:8000" method="post" enctype="multipart/form-data">
<p><input type="text" name="text" value="text default">
<p><input type="file" name="file1">
<p><input type="file" name="file2">
<p><button type="submit">Submit</button>
</form>
Create files to upload:
echo 'Content of a.txt.' > a.txt
echo '<!DOCTYPE html><title>Content of a.html.</title>' > a.html
Run:
nc -l localhost 8000
Open the HTML on your browser, select the files and click on submit and check the terminal.
nc prints the request received. Firefox sent:
POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: __atuvc=34%7C7; permanent=0; _gitlab_session=226ad8a0be43681acf38c2fab9497240; __profilin=p%3Dt; request_method=GET
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266
Content-Length: 554
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="text"
text default
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html
<!DOCTYPE html><title>Content of a.html.</title>
-----------------------------9051914041544843365972754266--
Aternativelly, cURL should send the same POST request as your a browser form:
nc -l localhost 8000
curl -F "text=default" -F "file1=#a.html" -F "file1=#a.txt" localhost:8000
You can do multiple tests with:
while true; do printf '' | nc -l localhost 8000; done
Many thanks to #Ciro Santilli answer! I found that his choice for boundary is quite "unhappy" because all of thoose hyphens: in fact, as #Fake Name commented, when you are using your boundary inside request it comes with two more hyphens on front:
Example:
POST / HTTP/1.1
HOST: host.example.com
Cookie: some_cookies...
Connection: Keep-Alive
Content-Type: multipart/form-data; boundary=12345
--12345
Content-Disposition: form-data; name="sometext"
some text that you wrote in your html form ...
--12345
Content-Disposition: form-data; name="name_of_post_request" filename="filename.xyz"
content of filename.xyz that you upload in your form with input[type=file]
--12345
Content-Disposition: form-data; name="image" filename="picture_of_sunset.jpg"
content of picture_of_sunset.jpg ...
--12345--
I found on this w3.org page that is possible to incapsulate multipart/mixed header in a multipart/form-data, simply choosing another boundary string inside multipart/mixed and using that one to incapsulate data. At the end, you must "close" all boundary used in FILO order to close the POST request (like:
POST / HTTP/1.1
...
Content-Type: multipart/form-data; boundary=12345
--12345
Content-Disposition: form-data; name="sometext"
some text sent via post...
--12345
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=abcde
--abcde
Content-Disposition: file; file="picture.jpg"
content of jpg...
--abcde
Content-Disposition: file; file="test.py"
content of test.py file ....
--abcde--
--12345--
Take a look at the link above.
There is an example of the multipart data (Angular):
trip-upload.component.html
<form [formGroup]="form" enctype="multipart/form-data" (ngSubmit)="submitForm()">
<div class="form-group">
<label for="trip">Trip:</label>
<input formControlName="name" type="text" id="trip" name="trip" placeholder="Name of the trip">
</div>
<div class="form-group">
<label for="guide">Guide for the trip:</label>
<input formControlName="guide" type="file" id="guide" name="guide" (change)="uploadFile($event,'guide')">
</div>
<div class="form-group">
<label for="photo">Guide for the trip:</label>
<input formControlName="photo" type="image" id="photo" name="photo" (change)="uploadFile($event, 'photo')">
</div>
<div class="form-group">
<button class="btn">Upload files</button>
</div>
</form>
2.trip-upload.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from "#angular/forms";
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'trip-upload',
templateUrl: './trip-upload.component.html',
styleUrls: ['./trip-upload.component.css']
})
export class TripUploadComponent implements OnInit {
public form: FormGroup;
constructor(public fb: FormBuilder, private http: HttpClient) {}
ngOnInit() {
this.form = this.fb.group({
name: [''],
photo: [null],
guide: [null]
})
}
uploadFile(event, fileType: string) {
this.updateFileFormControl(event, fileType);
}
submitForm() {
let formData: any = newFormData();
Object.keys(this.form.controls).forEach(formControlName => {
formData.append(formControlName, this.form.get(formControlName).value);
});
this.http.post('http://localhost:4200/api/trip', formData).subscribe(
(response) =>console.log(response),
(error) =>console.log(error)
)
}
private updateFileFormControl(event: Event, formControlName: string) {
const file = (event.target as HTMLInputElement).files[0];
this.form.controls[formControlName].patchValue([file]);
this.form.get(formControlName).updateValueAndValidity()
}
}
Multipart response
When Browser understand which enctype you use in your form for HTTP POST requests, user-agent configure list of name/value pairs to the server. Depending on the type and amount of data being transmitted, one of the methods will be more efficient than the other:
Related
Trying to send non-SOAP rate request to FedEx. The following XML works with SOAP Env and Body when I send their SOAP endpoint. It says in the documentation that they offer an XML-only solution, and that is formatted exactly the same as the SOAP request. Sending to https://wsbeta.fedex.com:443/xml. Please let me know if anyone has any insight.
<RateRequest>
<WebAuthenticationDetail>
<UserCredential>
<Key>omitted</Key>
<Password>omitted</Password>
</UserCredential>
</WebAuthenticationDetail>
<ClientDetail>
<AccountNumber>omitted</AccountNumber>
<MeterNumber>omitted</MeterNumber>
</ClientDetail>
<Version>
<ServiceId>crs</ServiceId>
<Major>28</Major>
<Intermediate>0</Intermediate>
<Minor>0</Minor>
</Version>
<RequestedShipment>
<ServiceType>FEDEX_2_DAY</ServiceType>
<Shipper>
<Address>
<StreetLines>4500 WEST 46TH STREET</StreetLines>
<City>CHICAGO</City>
<StateOrProvinceCode>IL</StateOrProvinceCode>
<PostalCode>60632</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</Shipper>
<Recipient>
<Address>
<City>TAMPA</City>
<StateOrProvinceCode>FL</StateOrProvinceCode>
<PostalCode>33616</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</Recipient>
<PackageCount>1</PackageCount>
<RequestedPackageLineItems>
<SequenceNumber>1</SequenceNumber>
<GroupPackageCount>1</GroupPackageCount>
<Weight>
<Units>LB</Units>
<Value>10</Value>
</Weight>
</RequestedPackageLineItems>
</RequestedShipment>
</RateRequest>
It looks like you're almost there. The root element of your xml document is missing the namespace http://fedex.com/ws/rate/v28.
In a SOAP message the namespace would be defined on the Envelope element. Because the data sent via the plain XML interface does not contain the wrapping Envelope and Body tags that are specific to SOAP, you have to add the namespace to the RateRequest element.
Your request should then be:
<RateRequest xmlns="http://fedex.com/ws/rate/v28">
<WebAuthenticationDetail>
<UserCredential>
<Key>omitted</Key>
<Password>omitted</Password>
</UserCredential>
</WebAuthenticationDetail>
<ClientDetail>
<AccountNumber>omitted</AccountNumber>
<MeterNumber>omitted</MeterNumber>
</ClientDetail>
<Version>
<ServiceId>crs</ServiceId>
<Major>28</Major>
<Intermediate>0</Intermediate>
<Minor>0</Minor>
</Version>
<RequestedShipment>
<ServiceType>FEDEX_2_DAY</ServiceType>
<Shipper>
<Address>
<StreetLines>4500 WEST 46TH STREET</StreetLines>
<City>CHICAGO</City>
<StateOrProvinceCode>IL</StateOrProvinceCode>
<PostalCode>60632</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</Shipper>
<Recipient>
<Address>
<City>TAMPA</City>
<StateOrProvinceCode>FL</StateOrProvinceCode>
<PostalCode>33616</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</Recipient>
<PackageCount>1</PackageCount>
<RequestedPackageLineItems>
<SequenceNumber>1</SequenceNumber>
<GroupPackageCount>1</GroupPackageCount>
<Weight>
<Units>LB</Units>
<Value>10</Value>
</Weight>
</RequestedPackageLineItems>
</RequestedShipment>
</RateRequest>
Also, don't forget to set the following headers in your request:
Accept: image/gif, image/jpeg, image/pjpeg, text/plain, text/html, */*
Content-Type: text/xml
My website is coded up using React.
I want the website to be static, so that I can just put it on Github and host it using Netlify.
The code below is my email form. Currently I am using a php script. I want to change this in order to make my website static. I really just want to know what I have to change the action to on the line action='./php/sendEmail.php' so that I no longer have to use php and can just access my sendinblue account(I heard it's free up to a certain point, so that's why I want to use it over mailgun) and send an email to my email address.
var Field = (props) => {
return (
<input id={props.id} className={props.className} aria-invalid="false" name={props.name} placeholder={props.name} data-aid={props.dataAid} type={props.type} onChange={(e) => dispatch(editValue(e.target.value,item.id)) }></input>
)
}
Field.defaultProps = { type: 'text'}
var EmailForm = (props) => {
return (
<div className="form" data-state="desktop left" data-dcf-columns="4">
<form action='./php/sendEmail.php' method="post" role="form" aria-label="contact form" className="form_style-wrapper" encType="multipart/form-data">
<Field id='field1' name='Name' dataAid='nameField' className='field_word required' />
<Field id='field2' name='Email' dataAid='emailField' className='field_word required' />
<Field id='field3' name='Phone' dataAid='emailField' type='hidden' className='field_word' />
<Field id='field4' name='Subject' dataAid='subjectField' className='field_word' />
<textarea name="Message" className="message" placeholder="Message" data-aid="messageField"></textarea>
<button className="sendButton" type="submit" name="submit" value="Send">Send</button>
</form>
</div>
)
}
EmailForm.defaultProps = {
action: 'handleSubmit'
}
You can configure the fields on your Sendinblue account and you can just use their REST APIs to send the emails.
You need to create and configure the template though.
https://developers.sendinblue.com/reference#sendtransacemail
An example API call:
curl --request POST \
--url https://api.sendinblue.com/v3/smtp/email \
--header 'accept: application/json' \
--header 'api-key:YOUR_API_KEY' \
--header 'content-type: application/json' \
--data '{
"sender":{
"name":"Sender Alex",
"email":"senderalex#example.com"
},
"to":[
{
"email":"testmail#example.com",
"name":"John Doe"
}
],
"subject":"Hello world",
"htmlContent":"<html><head></head><body><p>Hello,</p>This is my first transactional email sent from Sendinblue.</p></body></html>"
}'
Edit:
If you want to hit the API call, first you need to maintain the form state. So better would be to convert your functional component to stateful component.
You can read more about stateful components here: https://medium.com/#imletaconnoux/stateful-and-stateless-components-in-react-5da5cedb808f
Based on your request I made an example of making an api call (using axios) on form submit. You can use the reference and implement it to your app. You can use fetch api for the same instead of axios as well
https://codesandbox.io/s/misty-sun-2xpri
As Saurabh said - you can make API request to Sendinblue server which will send the email to you from their backend. You can send data from your form using fetch module very straightforward. You need to write javascript for collecting all field values from form in one object and then send it to destination url.
So I'm trying to create a Rails API with devise. My client side works well with GET, but I can't do POST type of calls. If I try to create a user I keep getting the following devise html form as response.
<h2>Sign up</h2>
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="RGZxLuXcOCbxKVdpFc/wSAKEp3wcxqpfnvmv0nstEcOppPyViWcmdu+dPt84XQeSkCmwRg0REZN+vTdX1j+MOQ==" />
<div id="error_explanation">
<h2>2 errors prohibited this user from being saved:</h2>
<ul><li>Email can't be blank</li><li>Password can't be blank</li></ul>
</div>
<div class="field">
<div class="field_with_errors"><label for="user_email">Email</label></div><br />
<div class="field_with_errors"><input autofocus="autofocus" type="email" name="user[email]" id="user_email" /></div>
</div>
<div class="field">
<div class="field_with_errors"><label for="user_password">Password</label></div>
<em>(4 characters minimum)</em>
<br />
<div class="field_with_errors"><input autocomplete="off" type="password" name="user[password]" id="user_password" /></div>
</div>
<div class="field">
<label for="user_password_confirmation">Password confirmation</label><br />
<input autocomplete="off" type="password" name="user[password_confirmation]" id="user_password_confirmation" />
</div>
<div class="actions">
<input type="submit" name="commit" value="Sign up" />
</div>
</form>
Log in<br />
Didn't receive confirmation instructions?<br />
Interestingly, at first I would get a "Can't verify CSRF token authenticity" error, so I added beforeSend to my JSON call. This would than change my call to OPTIONS, even though it was specified as a POST.
My call:
$(document).on('page:change', function(){
$('body').on('submit', 'form.create_new_user', function(e){
e.preventDefault();
var user_data = $(this).serializeJSON();
$.ajax({
url: Host.address + '/users',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
type: 'post',
datatype:'json',
data: {info: {full_name: user_data.full_name,
email: user_data.email,
password: user_data.password,
password_confirmation: user_data.password_confirmation}}
}).done(function(response){
console.log(response)
if (response.success.success) {
logInGoToWithNotice(response, '/', 'Thank you for signig up');
} else{
alert("User wasn't created. Please try again.")
};
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
And API does this:
Started OPTIONS "/users" for ::1 at 2015-09-30 14:09:49 +0200
ActionController::RoutingError (uninitialized constant RegistrationsController):
...
If I now remove beforeSend, I'd get the form again, just that "Can't verify CSRF token authenticity" error wouldn't be there anymore, but instead I'd get successful POST, that still returns html form.
Started POST "/users" for ::1 at 2015-09-30 14:25:57 +0200
Processing by Devise::RegistrationsController#create as */*
Parameters: {"info"=>{"full_name"=>"User Admin", "email"=>"user.admin#email.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
(0.1ms) BEGIN
(0.1ms) ROLLBACK
Rendered /Users/antonpot/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/devise-3.5.2/app/views/devise/shared/_links.html.erb (0.2ms)
Rendered /Users/antonpot/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/devise-3.5.2/app/views/devise/registrations/new.html.erb (3.5ms)
Completed 200 OK in 9ms (Views: 4.7ms | ActiveRecord: 0.2ms)
Now if I look into my Users Controller I can see I'm never even running my #create, as it never prints p "C"*99
class Users::RegistrationsController < Devise::RegistrationsController
def create
p "C"*99
user = User.new
user.full_name = user_params[:full_name]
user.email = user_params[:email].downcase
user.password = user_params[:password]
user.password_confirmation = user_params[:password_confirmation]
if user.save
render json: {success:{success: true, user_id: user.id, errorMessage: nil, errorNumber: 201}}
else
render json: {success:{success: false, errorMessage: user.errors.messages.to_s, errorNumber: 400}}
end
end
end
Why can't I call it? What do I need to do for this to work?
First
This issue because of cross-site-scripting problem. The client url is different than the api url. The HTTP method OPTIONS, is a pre-flight request that the browser makes to determine whether a cross-domain AJAX request should be allowed.
if cross-domain AJAX request is allowed the POST which is the actual request will be sent.
So you need to allow cross-domain AJAX request on your server.
Second
Make sure your routes.rb is set correctly.
You need to configure your devise and instruct him to use your customized registration_controller instead of default devise registration controller
adjust devise_for :users in your routes.rb to be something like the following:
devise_for :users, controllers: { registrations: "users/registrations"}
assuming that the customized registration class exist in the users folder
I'm playing with an AngularJs app, consuming an API REST with Slim PHP through $http (next step: $resource, I know). It all works fine when retrieving data from the API when the controller's written like this:
angular
.module("adminTaller", ['ngRoute'])
...
.controller("CustomersListController", ['$http', function($http){
var ctrl = this;
ctrl.customers = [];
$http.get('api/customers')
.success(function(data, status, headers, config){
ctrl.customers = data;
})
.error(function (data, status, headers, config) {
...
});
}]);
The 'customers' are displayed in this view:
<div ng-controller="CustomersListController as lstCtrl">
<input type="text" ng-model="searcher" />
<table>
<thead>
<tr>
<th>Apellido y Nombres</th>
<th>Domicilio</th>
<th>TE Celular</th>
<th>TE Comercial</th>
<th>TE Domicilio</th>
<th>Acciones</th>
</tr>
</thead>
<tbody ng-repeat="customer in lstCtrl.customers | filter:searcher | orderBy: fullname">
<tr>
<td>{{ customer.fullname }}</td>
<td>{{ customer.address }}</td>
<td>{{ customer.cellphone }}</td>
<td>{{ customer.businessphone }}</td>
<td>{{ customer.homephone }}</td>
<td>Editar</td>
</tr>
</tbody>
</table>
But in the moment I replace this by $scope:
angular
.module("adminTaller", ['ngRoute'])
...
.controller("CustomersListController", ['$http', '$scope', function($http, $scope){
var ctrl = $scope;
ctrl.customers = [];
$http.get('api/customers')
.success(function(data, status, headers, config){
ctrl.customers = data;
})
.error(function (data, status, headers, config) {
...
});
}])
, nothing seems to happen (I mean, no 'customer list' is loaded in the table), and Batarang gives me a weird warning (Chrome's console says nothing if Batarang is disabled):
Resource interpreted as Script but transferred with MIME type
application/x-js
My api/index.php sets the content-type to application/json:
$app = new Slim();
$app->response()->header('Content-Type', 'application/json');
$app->get('/customers', 'getCustomers');
$app->post('/customers', 'addCustomer');
$app->get('/customers/:id', 'getCustomer');
$app->put('/customers/:id', 'updateCustomer');
$app->delete('/customers/:id', 'deleteCustomer');
$app->run();
And in fact, the http request and http response headers look fine to me:
Remote Address:[::1]:8080
Request URL:http://localhost:8080/ng-taller/api/customers
Request Method:GET
Status Code:200 OK
**Request Headers**
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:es-ES,es;q=0.8,en;q=0.6,pt;q=0.4
Connection:keep-alive
Cookie:__ngDebug=true; PHPSESSID=b8cedd49edcc461fda62208e66cfb150; b8cedd49edcc461fda62208e66cfb150=DEFAULT%7C0%7C2M3TMlgUx3gTlaarYzHIdD28l8q9FTcNubt55%2BUGpAo%3D%7C7456bf61db3500c8bb7b3bc38082a470ce4a2ad3
Host:localhost:8080
Referer:http://localhost:8080/ng-taller/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
**Response Headers**
Connection:Keep-Alive
Content-Length:1027
Content-Type:application/json
Date:Wed, 04 Mar 2015 00:26:12 GMT
Keep-Alive:timeout=5, max=97
Server:Apache/2.4.4 (Win32) OpenSSL/0.9.8y PHP/5.4.19
Set-Cookie:b8cedd49edcc461fda62208e66cfb150=DEFAULT%7C0%7C2M3TMlgUx3gTlaarYzHIdD28l8q9FTcNubt55%2BUGpAo%3D%7C7456bf61db3500c8bb7b3bc38082a470ce4a2ad3; path=/
X-Powered-By:PHP/5.4.19
When I inspect the view's scope/model with Batarang, it shows:
{
lstCtrl: { }
customers:
[ {
id: 2
createdAt: 03-03-2015
fullname: Abelardini, Antonio Miguel
address: Virasoro Nº2533, Piso 3º - Rosario
cellphone: 341 (15)562-4484
businessphone: 341 430-4574
homephone:
observations:
logs:
[ ]
errors: 0
} , {
id: 1
createdAt: 03-03-2015
fullname: Alvarez, Juan
address: Gallo Nº1254 (entre Nuria y Valdez) - Rosario
cellphone: 341 (15)485-6225
businessphone:
homephone:
observations: Puntual en el pago
logs:
[ ]
errors: 0
} , {
id: 4
createdAt: 03-03-2015
fullname: Blanco de Escalada, María de las Mercedes
address: Av. Álvarez Thomas Nº1542, Piso 11º Depto.'A' - Rosario
cellphone: 341 (15)326-5484
businessphone:
homephone:
observations:
logs:
[ ]
errors: 0
} , {
id: 3
createdAt: 03-03-2015
fullname: Tirado López, Juan Manuel
address: Costanera Nº12 (Bajada de los Pescadores) - Rosario
cellphone: 341 (15)675-1125
businessphone:
homephone: 3205 124-8586
observations:
logs:
[ ]
errors: 0
} ]
}
I don't think this has anything to do with the mime type.
In your view, did you also change 'lstCtrl.customers' to just 'customers'? Otherwise you're pointing to a controller with nothing on it.
By the way, the way you had it the first time is the better approach. Using controllerAs syntax is preferred.
I'm writing a google apps script that is supposed to login to a website and then crawl the site and perform different actions.
The script successfully authenticates the website and completes several get methods, but when it tries to do a Post on a PostSelect drop-down box I'm getting a 500 response code.
My script follows:
function getRaceData(browser) {
var eId = "72370";
var selectNbr = "17";
var url = "http://tnetwork.trakus.com/tnet/t_Recap.aspx?EventID=" + eId;
var options = {
"method": "get",
// Set the cookies so that we appear logged-in
"headers": {
"Cookie": browser.cookie
},
"followRedirects" : false
}
var response = UrlFetchApp.fetch(url, options);
var html = response.getContentText();
var code = response.getResponseCode();
browser.html = html;
Utilities.sleep(1000);
var url = "http://tnetwork.trakus.com/tnet/t_Recap.aspx";
var payload = {"EventIDHidden" : eId, "PostSelect" : selectNbr};
var header = {"Cookie" : browser.cookie};
var options = {"method" : "post","headers" : header,"payload":payload,"followRedirects" : false};
Logger.log(UrlFetchApp.getRequest(url, options));
**var response = UrlFetchApp.fetch(url, options);** //This line causes 500 response code.
var html = response.getContentText();
var code = response.getResponseCode();
Logger.log(html);
}
Here is the error message I receive:
Request failed for http://tnetwork.trakus.com/tnet/t_Recap.aspx returned code 500. Truncated server response: <html>
<head>
<title>Runtime Error</title>
<style>
body {font-family:"Verdana";font-weight:normal;font-size: .7em;... (use muteHttpExceptions option to examine full response) (line 190, file "PostTest")
Update: Here is the Request Header that I was able to inspect by using Google Chrome Developer Tool Network selection:
POST /tnet/t_Recap.aspx HTTP/1.1
Host: tnetwork.trakus.com
Connection: keep-alive
Content-Length: 23079
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://tnetwork.trakus.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://tnetwork.trakus.com/tnet/t_Recap.aspx?EventID=72370
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: ASP.NET_SessionId=ayek3u55dsapvxe2t5kio0a3; __utma=260442568.815817400.1426544670.1426555405.1426631681.3; __utmc=260442568; __utmz=260442568.1426544670.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); userCredentials=username=MyUserName; .ASPXAUTH=D637CD472308F766FA8D267B85D8018028FC7981748D6B3EE9DC43166998E1F7F5A9B2A676CB1FD67560B86F40A633C4DDAAE6B13BBD0B07728E00E00977F271ABD5CC74D9AD88405CB7DD04F43F5EDDEF84EAAA14E5A3A638A744B0B76EA2FFEF2434A393E5FD699416FEA1A4EF0488671D82E4; __utma=190106350.902310224.1426544678.1426631684.1426643061.5; __utmb=190106350.3.10.1426643061; __utmc=190106350; __utmz=190106350.1426544678.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Also here is what I get when from logging the my UrlFetchApp.getRequest(url, options) function.
{headers{Cookie=ASP.NET_SessionId=zv2o2o55tgl2ctbse52vwln1;userCredentials=username=MyUserName;.ASPXAUTH=9980822DFE22641E55F3545CAEAFA748A39AC05A69F74A67BC656078C192769BB74A0B6F000241B36A670362019F5D14DCD1A6FD9E43718B5BC8AD89E9037DBEA1CE2A012AFFFC704D35C71EB82DAC18C644F0EBA34992604ABF2680DE97098C8BE3CB0BAEADD51C65E2EB0C8851C9E151337EA4, X-Forwarded-For=71.168.117.91}, useIntranet=false, followRedirects=false, payload=PostSelect=17&EventIDHidden=72370, method=post, validateHttpsCertificates=true, contentType=application/x-www-form-urlencoded, url=http://tnetwork.trakus.com/tnet/t_Recap.aspx}
I believe my syntax is correct and I've been able to pass the cookie successfully with all of the prior get statements and for the Login Post, so I'm not sure why this Post is failing. If anyone has any thoughts or has come across something similar, I'd appreciate your input
Update: I been review the page source a bit more carefully and wanted to share the following:
That page has a very large form that appears to include hidden inputs other than the ones in my Post statement (See below). Additionally, the Select statement has an onChange="UpdatePostView().
The UpdatePostView() is fairly straight forward:
function UpdatePostView()
{
document.TChartForm.submit();
}
Additional inputs include
<form name="TChartForm" method="post" action="t_Recap.aspx" id="TChartForm">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="eBDduN+CtRzqAG6KZjuiZQhrTRokT2cqgaabq7fy3oSZaeE+O83K0QTLMCessWWC27snsJrG607bicynkgi9j1+4a++Gpz8+S5CeD9hhjzEEH8NuJUOtgPDOV5QFkqNrGVX4g+1YehwD+A5V7K5o+j5zva1cABIEhbHoxAqKrETpZgNVDjpZWTc+Hl4NaG1aTGpARvzUTgU23CvtxwYrIAH2CVuDHeqMTo0B9aOB73e4liFQtMnj/64ETSf2eGvbgmBLKaPHDxDrRCjMRLBinWRn94lLF6Cf3ZUVoQTMU4IgpQ==" />
<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="696C68DE" />
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="pv3ODePltFyNx0vx4zFN/4siIO4psfHIMLofolP8T27GF4kko1Ol7wQaPQ0nuAtGCoANyMCeHmV0ecBuv/CGuv4YF2jZSIQmPtNFmSpDEh/EJvyVxljXY2d+gv8=" />
<input type="hidden" name="EventIDHidden" id="EventIDHidden" value="72370" />
<input type="hidden" id="SessionUser" value="MyUserName" />
<input type="hidden" id="SessionAccessStr" value="MyUserName" />
<div id="DisplayArea"><table class="headLinks" align="center" width="97%" cellpaddiong="0" cellspacing="0"><tr><td width="50%"><div align="left"><a style="white-space:nowrap;" href="t_RaceDay.aspx?VenueID=24&Type=TBRED&Date=03/01/2015">«««More Aqueduct Races on 03/01/2015</a></div><br /><div align="left"><a href="previous.html" onClick="history.back();return false;">«««Previous Page
</a></div><p /></td><td valign="top"><div align="right"><b>
T-Net Member: myTnetMemberName </b></div></td></tr></table><table align="center" class="topThreeContainer" cellpadding="2" cellspacing="0"><tr align="center" class="topThreeTitle"><td style="white-space:nowrap">Track</td><td style="white-space:nowrap">Official Results</td><td style="white-space:nowrap">Trakus Times</td></tr><tr><td><img src="images/TrackLogos/24.gif" /></td><td valign="top"><table cellpadding="2"><tr class="topThreeHead"><td><div style="width:40px;">Win</div></td><td><div style="width:40px;">Place</div></td><td><div style="width:40px;">Show</div></td></tr><tr><td><img width="30" height="27" src="images/HorseRacingTiles/Trakus/3.gif" /></td><td><img width="30" height="27"
additional form data omitted due to space constraints....
<select runat="server" autopostback="true" style="width:250px" id="PostSelect" onChange="UpdatePostView();" name="PostSelect"><option value="0">Race Summary</option><option value="2">1/16 Mile</option><option value="3">1/8 Mile</option><option value="4">3/16 Mile</option><option value="5">1/4 Mile (POC)</option><option value="6">5/16 Mile</option><option value="7">3/8 Mile</option><option value="8">7/16 Mile</option><option value="9">1/2 Mile (POC)</option><option value="10">9/16 Mile</option><option value="11">5/8 Mile</option><option value="12">11/16 Mile</option><option value="13">3/4 Mile (POC)</option><option value="14" selected="">13/16 Mile</option><option value="15">7/8 Mile (POC)</option><option value="16">15/16 Mile</option><option value="17">1 Mile (Finish)</option></select>
additional form data omitted due to space constraints....
Race Notes: </div></td></tr><tr><td align="right" colspan="2"><div align="right" style="font-size:8px;font-weight:bold;">Updated 3/1/2015 6:29:10 PM GMT
</div></td></tr></table></div></form>
Given this new information, I have a couple additional questions:
Do I need to add the other hidden inputs to my payload or are they embedded in the Html and therefore get submitted anyway.
Does the urlfecthapp command fire the onchange event or do I have to do something special to fire that.
What is the role of the ViewState, ViewStateGenerator and EventValidation. These were present in the login form, but I did not need to include them in the payload to login successfully.
Here is my full script if anyone would like to try it and see the results for themselves.
Full Script
Thanks again.