Is that even possible? what I need to pass to mechanize? With what url I can start then?
I cannot manage (so far) to log into one website using mechanize so I was thinking if I can do this little workaround. I believe I can capture all cookies and everything else and then pass it to ruby/mechanize to do the rest ...
screenshots below are made using firebug ( Firebug logs the POST or GET request, the response headers )
login that works = just one line
and html for login that works
<script type="text/javascript" src="clientscript/vbulletin_md5.js?v=3612"></script>
<table cellpadding="0" cellspacing="1" border="0">
<tr>
<td class="smallfont" align="left"><label for="navbar_username">User Name</label></td>
<td class="smallfont" align="left" colspan="2"><label for="navbar_password">Password</label></td>
</tr>
<tr>
<td><input type="text" class="button" name="vb_login_username" id="navbar_username" size="10" accesskey="u" tabindex="101" value="User Name" onfocus="if (this.value == 'User Name') this.value = '';" /></td>
<td><input type="password" class="button" name="vb_login_password" id="navbar_password" size="10" accesskey="p" tabindex="102" /></td>
<td class="smallfont" align="left" valign="middle"><input type="submit" class="button" value="Log in" tabindex="103" title="Enter your username and password in the boxes provided to login, or click the 'register' button to create a profile for yourself." accesskey="s" />
<label for="cb_cookieuser_navbar">
<input type="checkbox" name="cookieuser" value="1" tabindex="103" id="cb_cookieuser_navbar" accesskey="c" />Save?</label>
<input type="hidden" name="s" value="" />
<input type="hidden" name="securitytoken" value="1cbc0286417d97b4eb43ee0b0c2b54e7c615e5b8" />
<input type="hidden" name="do" value="login" />
<input type="hidden" name="vb_login_md5password" />
<input type="hidden" name="vb_login_md5password_utf" /></td>
</tr>
</table>
</form>
<!-- / login form -->
login that doesn't work for me
and it's html
<form class="login" method="post"> <fieldset>
<legend>Members Login</legend>
<div>
<label for="auth_username">Username</label> <input id="auth_username" name="auth_username">
</div>
<div>
<label for="auth_password">Password</label> <input id="auth_password" name="auth_password" type="password">
</div>
</fieldset>
<div class="buttons">
<input name="auth_login" type="submit" value="Login"><p class="note">Forgot your password?</p>
</div>
</form>
my script is almost the same in both cases.
require 'rubygems'
require 'mechanize'
#agent = WWW::Mechanize.new
agent = WWW::Mechanize.new
page = agent.get("http://www.vbulletin.org/forum/index.php")
login_form = page.form_with(:action => 'login.php?do=login')
puts
login_form.fields.each { |f| puts "#{f.name} : #{f.value}" }
login_form['vb_login_username'] = 'user name'
login_form['vb_login_password'] = ''
page = agent.submit login_form
output = File.open("login.html", "w") {|f| f.write(page.parser.to_html) }
mechanize log for the login that doesn't work
INFO -- : Net::HTTP::Get: /login?auth_successurl=http://www.somedomain.com/forum/yota?baz_r=1
DEBUG -- : request-header: accept-language => en-us,en;q=0.5
DEBUG -- : request-header: connection => keep-alive
DEBUG -- : request-header: accept => */*
DEBUG -- : request-header: accept-encoding => gzip,identity
DEBUG -- : request-header: user-agent => WWW-Mechanize/0.9.3 (http://rubyforge.org/projects/mechanize/)
DEBUG -- : request-header: accept-charset => ISO-8859-1,utf-8;q=0.7,*;q=0.7
DEBUG -- : request-header: host => www.somedomain.com
DEBUG -- : request-header: keep-alive => 300
DEBUG -- : Read 400 bytes
DEBUG -- : Read 1424 bytes
DEBUG -- : Read 2448 bytes
DEBUG -- : Read 3211 bytes
DEBUG -- : response-header: vary => Accept-Encoding
DEBUG -- : response-header: cache-control => no-store, no-cache, must-revalidate, post-check=0, pre-check=0
DEBUG -- : response-header: connection => close
DEBUG -- : response-header: expires => Thu, 19 Nov 1981 08:52:00 GMT
DEBUG -- : response-header: content-type => text/html; charset=utf-8
DEBUG -- : response-header: date => Fri, 29 Jan 2010 23:43:12 GMT
DEBUG -- : response-header: content-encoding => gzip
DEBUG -- : response-header: server => Apache/2.2.3 (CentOS)
DEBUG -- : response-header: content-length => 3211
DEBUG -- : response-header: set-cookie => PHPSESSID=7cfilg86ju2ldcgso22246hpu4; path=/, WebStats:visitorId=lSMkcwuSWEE%3D; expires=Mon, 27-Jan-2020 23:43:12 GMT; path=/, WebStats:sessionId=%2B2HHK296t%2BQ%3D; expires=Mon, 27-Jan-2020 23:43:12 GMT; path=/
DEBUG -- : response-header: accept-ranges => bytes
DEBUG -- : response-header: pragma => no-cache
DEBUG -- : gunzip body
DEBUG -- : saved cookie: PHPSESSID=7cfilg86ju2ldcgso22246hpu4
DEBUG -- : saved cookie: WebStats:visitorId=lSMkcwuSWEE%3D
DEBUG -- : saved cookie: WebStats:sessionId=%2B2HHK296t%2BQ%3D
INFO -- : status: 200
DEBUG -- : query: "auth_username=radek&auth_password=mypassword"
INFO -- : Net::HTTP::Post: /login?auth_successurl=http://www.somedomain.com/forum/yota?baz_r=1
DEBUG -- : request-header: accept-language => en-us,en;q=0.5
DEBUG -- : request-header: connection => keep-alive
DEBUG -- : request-header: accept => */*
DEBUG -- : request-header: accept-encoding => gzip,identity
DEBUG -- : request-header: content-type => application/x-www-form-urlencoded
DEBUG -- : request-header: user-agent => WWW-Mechanize/0.9.3 (http://rubyforge.org/projects/mechanize/)
DEBUG -- : request-header: cookie => WebStats:sessionId=%2B2HHK296t%2BQ%3D; WebStats:visitorId=lSMkcwuSWEE%3D; PHPSESSID=7cfilg86ju2ldcgso22246hpu4
DEBUG -- : request-header: referer => http://www.somedomain.com/login?auth_successurl=http://www.somedomain.com/forum/yota?baz_r=1
DEBUG -- : request-header: accept-charset => ISO-8859-1,utf-8;q=0.7,*;q=0.7
DEBUG -- : request-header: content-length => 43
DEBUG -- : request-header: host => www.somedomain.com
DEBUG -- : request-header: keep-alive => 300
DEBUG -- : Read 650 bytes
DEBUG -- : Read 1674 bytes
DEBUG -- : Read 2698 bytes
DEBUG -- : Read 3211 bytes
DEBUG -- : response-header: vary => Accept-Encoding
DEBUG -- : response-header: cache-control => no-store, no-cache, must-revalidate, post-check=0, pre-check=0
DEBUG -- : response-header: connection => close
DEBUG -- : response-header: expires => Thu, 19 Nov 1981 08:52:00 GMT
DEBUG -- : response-header: content-type => text/html; charset=utf-8
DEBUG -- : response-header: date => Fri, 29 Jan 2010 23:43:13 GMT
DEBUG -- : response-header: content-encoding => gzip
DEBUG -- : response-header: server => Apache/2.2.3 (CentOS)
DEBUG -- : response-header: content-length => 3211
DEBUG -- : response-header: accept-ranges => bytes
DEBUG -- : response-header: pragma => no-cache
DEBUG -- : gunzip body
INFO -- : status: 200
Your problem is very likely from the fact that Mechanize only tracks cookies created with the Set-Cookie HTTP header. It cannot handle cookies created by JavaScript.
Yes, capturing cookies (eg, through FireCookies Plugin in Firefox), and manually passing it to mechanize could work for most cases.
Related
I am building a web application, on the front end side I am using Nuxt js (Laravel sanctum package) and on the back end side I am using Laravel sanctum
I configure the front-end and back-end both, cookies are also generating correctly but I unable to hit the Laravel route. When I send the request it return 200 code with the cookie but Laravel method return nothing, it's means that my app is not hitting the Laravel method. After clicking on the login button I am redirected to the dashboard but console return no response with no error.
My login page headers
Request URL: http://localhost:3000/http://localhost:8000/api/admin/login
Request Method: POST
Status Code: 200 OK
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin
Accept-Ranges: none
Connection: keep-alive
Content-Length: 2779
Content-Type: text/html; charset=utf-8
Date: Mon, 24 May 2021 16:08:05 GMT
ETag: "adb-BuJ98IeCR24JYV5bINxcPHcMBsc"
Accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,la;q=0.8,mt;q=0.7,id;q=0.6,pl;q=0.5,fr;q=0.4
Connection: keep-alive
Content-Length: 2
Content-Type: application/json
Cookie: auth.strategy=laravelSanctum; auth._token_expiration.laravelSanctum=false; auth._token.laravelSanctum=false
Host: localhost:3000
Origin: http://localhost:3000
Referer: http://localhost:3000/admin/login
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
X-Requested-With: XMLHttpRequest
Api.php
Route::prefix('admin')->group(function(){
Route::post('/login', [LoginController::class, 'login'])->name('admin.login');
});
LoginController.php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class LoginController extends Controller
{
public function login(Request $request)
{
return "Login Success";// Just for testing
}
}
nuxt.config.js
modules: [
'bootstrap-vue/nuxt',
'#nuxtjs/axios',
'#nuxtjs/auth-next'
],
auth: {
strategies: {
'laravelSanctum': {
provider: 'laravel/sanctum',
url: '/http://localhost:8000',
endpoints:{
login:{
url:'/api/admin/login',
withCredentials: true,
method: 'post',
},
logout:{
url:'admin/logout'
},
user:{
url:'/user'
},
},
user:{
property:Array
}
},
},
redirect:{
login: '/admin/login',
logout: '/',
home: '/'
}
},
Login.vue
<template>
<div>
<index />
<b-container>
<b-row>
<b-col cols="*" sm="*" md="6" lg="6" class="mx-auto mt-5 offset-2">
<b-card title="Login Page">
<b-form>
<b-form-group>
<label for="text-email">Email</label>
<b-form-input type="email" v-model="form.email" aria-describedby="email-help-block"></b-form-input>
</b-form-group>
<b-form-group>
<label for="text-password">Password</label>
<b-form-input type="password" v-model="form.password" aria-describedby="password-help-block"></b-form-input>
</b-form-group>
<b-form-group>
<b-button type="submit" #click.prevent="login()" block variant="primary">Login</b-button>
</b-form-group>
</b-form>
</b-card>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import index from '../index.vue'
export default {
components: { index },
data(){
return {
form:{
email: '',
password: '',
}
}
},
methods:{
async login(){
await this.$auth.loginWith('laravelSanctum', {
data: {
email: this.email,
password: this.password
}
})
.then( (response) => {
this.$router.push('/admin/dashboard')
console.log( response );
})
.catch( (error) => {
console.log( error );
})
}
}
}
</script>
package.json
"dependencies": {
"#nuxtjs/auth-next": "5.0.0-1620773067.c356fed",
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.9.1",
"nuxt": "^2.15.3"
},
My request URL is invalid, the spa domain added in the URL that's why my spa is not sending requests to the server.
Request URL: http://localhost:3000/http://localhost:8000/api/admin/login
TO
Request URL: http://localhost:8000/api/admin/login
That works fine.
I'm using Semantic UI and Laravel 6
I got this html code:
<div class="field">
<label for="realTime">Tiempo real</label>
<input type="text" id="realTime" name="schedules[{{ $service->id_service }}][realTime]" required/>
</div>
<div class="field">
<label for="delayTime">Tiempo de demora</label>
<input type="text" id="delayTime" name="schedules[{{ $service->id_service }}][delayTime]" required/>
</div>
<div class="field">
<label for="deathTime">Tiempo muerto</label>
<input type="text" id="deathTime" name="schedules[{{ $service->id_service }}][deathTime]" required/>
</div>
And I'm sending this info by ajax using this code:
$.ajax({
url: "...",
data: {
"formData": $(formClass).form('get values')
},
success: function (response) {
// skipped code
}
});
But when I dump the request data in controller using:
dd($request->input('formData'));
I got this result:
array:1 [
"formData" => array:1 [
"schedules[1" => array:3 [
"realTime" => "12:00:00"
"delayTime" => "13:00:00"
"deathTime" => "14:00:00"
]
]
]
Instead of:
array:1 [
"formData" => array:1 [
"schedules" => array:1 [
1 => array:3 [
"realTime" => "12:00:00"
"delayTime" => "13:00:00"
"deathTime" => "14:00:00"
]
]
]
]
I checked out the content sent shown in Headers tab (using Chrome) and I found this:
formData[schedules[1][realTime]]: 12:00:00
formData[schedules[1][delayTime]]: 13:00:00
formData[schedules[1][deathTime]]: 14:00:00
I also realized that if I put the name as "schedules][{{ $service->id_service }}][realTime]" (putting an extra closing bracket after "schedules"), the dump shows the array correctly.
Any solution?
I have asp.net web api server run in debug made and separate project uses Angular 5 - 5.2.11.
I try to delete data and I can't, but when I use postman it work.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
#Injectable()
export class UserService { private getUserSucess: any;
private _headers = {
headers: new HttpHeaders().set('Content-Type', 'application/json')
};
private headers={
headers: new HttpHeaders({
'Content-Type': 'application/json'
});
}
constructor(private http: HttpClient) { }
deletUserHttp() {
return this.http.delete( 'http://localhost:52817/api/users/1' ).subscribe(deleteSucess.bind(this), deleteError.bind(this));
function deleteSucess(resp) {
debugger;
return resp;
}
function deleteError(resp) {
//resp = HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false,
debugger;
return resp;
}
}
I also try with: this._headers and this.headars the result was the same:
HttpErrorResponse { headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false,
405 Method Not Allowed:
Request URL: http://localhost:52817/api/users/1
Request Method: OPTIONS
Status Code: 405 Method Not Allowed
Remote Address: [::1]:52817
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Allow: GET,PUT,DELETE
Cache-Control: no-cache
Content-Length: 71
Content-Type: application/json; charset=utf-8
Date: Sat, 16 Jun 2018 05:43:07 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
X-SourceFiles: =?UTF-8?B?
QzpcVXNlcnNcQmV0bWlyYVxzb3VyY2VccmVwb3NcVXNlcnNcVXNlcnNcYXBpXHVzZXJzXDE=?=
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.6
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: localhost:52817
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
web.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
<rewrite>
<outboundRules>
<clear />
<rule name="AddCrossDomainHeader">
<match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTP_ORIGIN}" pattern="(http(s)?://((.+\.)?domain1\.com|(.+\.)?domain2\.com|(.+\.)?domain3\.com))" />
</conditions>
<!--<match serverVariable="RESPONSE_Access-Control-Allow-Methods" pattern="GET, POST, PUT, DELETE, OPTIONS" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTP_ORIGIN}" pattern="(http(s)?://((.+\.)?domain1\.com|(.+\.)?domain2\.com|(.+\.)?domain3\.com))" />
</conditions>-->
<action type="Rewrite" value="{C:0}" />
</rule>
</outboundRules>
</rewrite>
...
Please help me because I really don't know what is wrong.
Please refer to this document for enabling CORS for your asp.net webapi. Once CORS is enabled you should be able to delete using Angular app.
I am making an ajax request with Csrf component load in my AppController
However I get the error {"message":"CSRF token mismatch.","url":"\/module_slides\/loadDeck.json","code":403}
Here is the request header
POST /module_slides/loadDeck.json HTTP/1.1
Host: www.hotelieracademy.com
Connection: keep-alive
Content-Length: 18
Origin: https://www.hotelieracademy.com
X-XSRF-TOKEN: 3d3901b1de9c5182dce2877c9e1d9db36cdf46a6
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: https://www.hotelieracademy.com/courses_employees/player/70
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: csrfToken=3d3901b1de9c5182dce2877c9e1d9db36cdf46a6; CAKEPHP=3n6lpi94hrdgsg8mv4fsnp1m30; _ga=GA1.2.2010364689.1424741587
My ajax code
$.ajax({
url: '/module_slides/loadDeck.json',
type: 'POST',
headers: { 'X-XSRF-TOKEN' : this.csrfToken },
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', this.csrfToken);
},
dataType: 'json',
data: {
I have left the beforeSend: as suggest by another post but does not seem to alter the header so I added headers:
I use a hidden input to get the CsfR token to use in my js code
<input id="csrfToken" type="hidden" value="<?= $this->request->getParam('_csrfToken') ?>">
I've met the same problem.
Probably, this is the answer to add "_csrfToken":"xxxxxxx" to data{}.
$.ajax({
url: '/module_slides/loadDeck.json',
type: 'POST',
headers: { 'X-XSRF-TOKEN' : this.csrfToken },
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', this.csrfToken);
},
dataType: 'json',
data: {
"_csrfToken":"3d3901b1de9c5182dce2877c9e1d9db36cdf46a6"
}
This is my blog.but it's Japanese Only.
http://www.tsuji75.com/?p=62
Here is my solution.
For the CSRF token, I am creating an empty cakephp form and it provides the CSRF token.
Also, I do not unlock any action. instead I do unlock fields.
Ref: https://book.cakephp.org/3.0/en/controllers/components/security.html
here is my working example.
Scenario: Ajax call to add an event was failing due to cakephp 3 CSRF token mismatch issue.
Solution:
I have created an empty form in the view so it can provide CSRF token for my form and then attached the required input fields before the ajax. In the form itself, I unlocked the hidden fields. This way I do not disturb CSRF component.
In VIEW
<?= $this->Form->create(false, [
'id' => "ajaxForm",
'url' => ['controller' => 'XYZ', 'action' => 'add'],
'class'=> "addUpdateDeleteEventForm"
] );
$eventdata = [];
?>
<?= $this->Form->unlockField('id'); ?>
<?= $this->Form->unlockField('start'); ?>
<?= $this->Form->unlockField('end'); ?>
<?= $this->Form->unlockField('title'); ?>
<?= $this->Form->button('Submit Form', ['type' => 'submit']);?>
<?= $this->Form->end(); ?>
Ajax:
var id = $("<input>")
.attr("type", "hidden")
.attr("name", "id").val(id);
var titleField = $("<input>")
.attr("type", "hidden")
.attr("name", "title").val(title);
var startTime = $("<input>")
.attr("type", "hidden")
.attr("name", "start").val(start);
var endTime = $("<input>")
.attr("type", "hidden")
.attr("name", "end").val(end);
$('#ajaxForm').append(id);
$('#ajaxForm').append(titleField);
$('#ajaxForm').append(startTime);
$('#ajaxForm').append(endTime);
var ajaxdata = $("#ajaxForm").serializeArray();
$.ajax({
url:$("#ajaxForm").attr("action"),
type:"POST",
data:ajaxdata,
dataType: "json",
success:function(response)
{
toastr.success(response.message, response.title);
calendar.fullCalendar("removeEvents");
calendar.fullCalendar("refetchEvents");
},
error: function(response)
{
toastr.error(response.message, response.title);
}
});
Hope this helps.
I have a simple form with 3 fields, one containing a pickadate(), the other a pickatime() and the last a simple number.
When I try to post my form, I get a bad request error.
Request dedails
Request URL:http://localhost:8080/CapTheater/admin/addProjection
Request Method:POST
Status Code:400 Mauvaise RequĂȘte
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/
Accept-Encoding:gzip,deflate,sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:39
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=EA0DDBF444BC5A4CA416715D72FF9D71
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/CapTheater/showMovie/1
User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Form Data
day:2014-01-01
date:09:30
place_nbr:0
Response Headers
Connection:close
Content-Length:986
Content-Type:text/html;charset=utf-8
Date:Mon, 30 Dec 2013 23:35:24 GMT
Server:Apache-Coyote/1.1
Here is the form :
<form:form method="post" action="../admin/addProjection" id="add_movie_form">
<form:label id="dayLabel" path="day">Date : </form:label>
<form:input type="text" id="dayInput" path="day" />
<form:label id="startLabel" path="date">Start : </form:label>
<form:input type="text" id="startInput" path="date" />
<form:label id="sizeLabel" path="place_nbr">Steats availables :
<form:input type="text" id="sizeInput" path="place_nbr" />
<input type="submit" class="btn btn-primary"value="Save" />
</form:form>
<script type="text/javascript">
$("#tabs").tabs();
$("#dayInput").pickadate({
format : 'yyyy-mm-dd',
min : new Date()
});
$("#startInput").pickatime({
format : 'HH:i',
min: [8, 0],
max: [22, 30]
});
</script>
The controller
#RequestMapping(value = "admin/addProjection", method = RequestMethod.POST)
public String addProjection(
#ModelAttribute("command") final Projection projection,
final ModelMap model)
{
projectionDao.save(projection);
return "/showMovie/" + projection.getMovieId() + "#tabs-4";
}
Finally, in the bean
public class Projection implements Comparable<Projection>
{
#DateTimeFormat(pattern = "yyyy-MM/dd")
private Date day;
public Date getDay()
{
return day;
}
public void setDay(Date day)
{
this.day = day;
}
}
Thank you :)