I was trying to implement page caching in Yii2 advanced project and everything seemed to be super cool. And suddenly I was hit by a strange issue.
Case: On the homepage of the website there is some dynamic data like showing records from DB, info of current user like the name (if the user is logged-in) and some static content. Also, a search input field which fetches result using AJAX call.
To speed page loading I implemented PageCaching provided by Yii2. And all worked good. But one issue I got stuck at is that after user log-in the ajax call didn't work and gave Error:
Bad Request (#400): Unable to verify your data submission.
I get this error till cache is refreshed after the set duration or I disable cache.
Is this issue related to cookie/session or something else? How to resolve it?
The 400 Bad Request is because the csrf-token is not being sent, with the request which is required to prevent cross-site attacks by Yii whenever you use POST to submit a page or ajax request, if you create an ActiveForm then it creates an input automatically with the token value.
You didn't add the code that you are using for the ajax call so not clear if you are using it for only one field or the whole form, so I would suggest the concerning part only.
You need to send the csrf-token and you can get it via javascript using yii.js and calling these 2 methods
yii.getCsrfParam() to get the parameter name of the token
yii.getCsrfToken() to get the token or actual value of the csrf-token
The csrfParam name is configured inside your frontend/config.php or config/web.php depending on the app you are using (advance /basic) under the request component like below
'components'=>[
......
......
'request' => [
'csrfParam' => '_csrf-frontend',
],
......
......
]
So what you need to do is either change the request method from POST to GET and send data via query string or use the following way to send the POST request.
Note: You should change the URL and add the csrf data into your existing data that you are sending with the request
let data={};
data[yii.getCsrfParam()]=yii.getCsrfToken();
$.ajax(
{
method:"POST",
url:"/site/test",
data:data,
success:function(data) {
console.log(data);
},
error:function(jqXHR,textStatus,errorThrown) {
console.log(jqXHR,textStatus,errorThrown);
}
}
);
If you have a test action inside the SiteController with the following code, then the above ajax call should show you the $_POST array inside the console with the csrf param and token value as key=>value.
public function actionTest()
{
print_r($_POST);
}
Related
I have a Core 2.2 Web API project as my back-end for an Angular front-end. On one screen I allow the user to select x amount of records for processing from a data grid. Before the records can be processed I need to check the database to see if records exist in another table by passing a list of 3 fields (intelligent key) to my API. I put this list into an object array, do a Json.stringify on that object and send it to my API as a Get request. This works fine as long as I select 1-3 records. As soon as I select 4 or more records I get "Access to XMLHttpRequest at 'request url' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
My Cors policy indicates it should let anything through and I'm also confused by why 1-3 records works fine.
In my startup.cs -> ConfigureServices method I have the Cors policy defined like so:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
In my Angular service I make this call which serializes by array (apiUrl is my url to call my API: https://localhost/api/controller)
getRecordsByIntelligentKey(intelligentKey: IntelligentKey[]): Observable<Record[]>{
const jsonObject = JSON.stringify(intelligentKey);
const url = `${apiUrl}/${jsonObject}`;
return this.http.get<Record[]>(url).pipe(
tap(_ => console.log('fetching records based on intelligent key')),
catchError(this.handleError('getRecordsByIntelligentKey', []))
);
}
In my controller GET action I deserialize my string. I mean I'd like to pass an object but I think I need to do a POST for that.
[HttpGet("{jsonObject}")]
public ActionResult<List<Commission>> GetByCustomerAndTerritoryManager(string jsonObject)
{
try
{
var intelligentKey = JsonConvert
.DeserializeObject<List<ThreeFields>>(jsonObject);
return _repo.GetRecordsByIntelligentKey(intelligentKey);
}
catch (Exception ex)
{
_logger.Error(ex, "Error retrieving records.");
throw;
}
}
Now my problem is the user could select 1000s of records. When I select over 1000 records I just get ERR_CONNECTION_RESET probably due to the querystring being way too long.
I'm thinking I need an object but everything I've researched seems to advise against doing that with a GET and using the POST request instead. Problem is, it's a restful API and I'm already using the POST request for the processing portion. I guess I could use PUT or DELETE but it just feels wrong. I'm going to wire up the PUT right after I post this question to see if it will work but ultimately I'd like to find the correct solution for this.
UPDATE: The PUT method works fine even with over 1000 records selected so I guess this will be my interim solution for now. I still feel like there's code smell and would love to use a GET but at least this allows me to proceed.
It took me a while to understand this, being that it was a little obvious. I will answer myself, so other can benefit of the answer and ofcourse to see if there's a better way to do this. The problem was based on Axios/Yii2 but I guess this will apply equally to other frontend libraries/frameworks sending data to Yii2.
I needed to post data from a small form made on Vuejs, sending the request Axios to a Action/Controller on Yii2, so data is sent on a simple POST request and the post is getting to the controller, but I was not able to receive the data on the action, $_POST | $post arrives empty (checked using xdebug).
As much as I remember, this had something to do with security. But I already tried by disabling public $enableCsrfValidation, so that was not the problem.
public $enableCsrfValidation = false;
But no matter what, data was not being added to the request/post data inside Yii2.
The following Image, explains the problem you will find there:
The Axisos method that sends the post with test data.
The Yii2 Action stpoed at the place, I should be able to see data.
The capture of the xdebug variables and data for the request.
The capture of Chrome where you can check the payload is sent.
The answer is as I said "kind of obvious", but I could not see that, and I am sure some other devs will probably fall on this.
After searching like crazy and asking everyone, I tried sending the request by using Postman app, yup the best thing I know to test apis.
Dont forgue to add the xdebug cookie to be able to debug your PHP Endpoint.
There I found the first clue «the obvious part», I was not sending data as a form-data, Axios and other libraries, send the data as a raw (application/json) payload.
This means that Yii2 will no be able to find the data inside the post request, yes its there but Yii2 magic will not work, neither you will find this data inside $GLOBALS or in $_POST.
So reading the Yii2 documentation I found that inside request I can use a function that will help me recovering the Raw data, so to do this use the following line:
$raw_data = Yii::$app->request->getRawBody();
Now, that data gets to you as a simple, raw json string, so use the power of PHP to parse it to an object.
$object= json_decode($raw_data );
And finally use the data inside by calling the properties you look for, sent on the pay load:
Json Payload:
{
"msg":"This is my payload",
"id":"11"
}
To use it:
echo $object->{'msg'}; // prints: This is my payload
So that's the way to handle that, now I would like some other points of view to see if there's a better way or cleaner way to do this. Hope it helps.
I am trying to load some images from a file on a server to display on a list. I get 404 not found error
after asking in forums, I get that the request URL is wrong, it looks inside the localhost not inside the json file.
https://filehost.net/db54d37849f75ddd
in the code I have a service which returns a response. I use that response in a controller and to the view. I get other information but no images
if anybody has a solution that would be really great.
please attach also some code, in this way it is easier to help you. About the problem, do have correct form of $http GET request? I mean does your get request points to correct address on server? something like this
`
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
`
you can check more here Angular $http
I have a form in my view.
I only want to submit the data, without the rendering of any other template or following the post request to a new URL.
Are there any solutions to this besides structuring the whole thing about AJAX?
EDITED:
I have a multipart form..
part 1 submits data to a cache (using AJAX), then I use JQuery to hide that part of the form and show part 2. Part 2 is what submits the main data to a rails controller. That controller action is responsible for pulling the previously cached data and sending a POST request to an API that stores part 1 and part 2 in a database.
What I would like to do:
Have the rails app receive the form data and process the request, then I could use an AJAX request to append data to the page (clearing out the form, and showing the compiled data submitted if the post was successful, and an error message if not).
UPDATE:
I have tried the following and it does nothing when clicked:
$('#button').click(function(event){
event.preventDefault();
$(this).submit(); //also tried .send();
});
you could either redirect to the same page or maybe do this
$('#selector').click(function(event){
event.preventDefault();
//Do other thing, or maybe nothing
});
In your controller do the following:
respond_to do |format|
format.all { render nothing: true, status: 200 }
end
I deployed to AWS for first time, and experienced a very strange behavior. I use AngularJS and there is a function that performs $http service call with DELETE method specified.
var fn = function () {
$http({ method: "DELETE", url: "/active/route/"})
...
and when I perform it, request hangs for a while and get refused.
I've changed fn to
function () {
$http({ method: "POST", url: "/active/route/delete"})
...
And it worked just fine.
I want to know if beanstalk has specific policy about some HTTP methods or what was causing this behavior?
I know this has been fairly inactive for a while but I figured out what was happening in my case and thought I would post it here for anyone else who needs it. Basically http DELETE methods do not play nice if you are sending a request that contains a request body. Pure RESTful services should be employing the URI to pass object ids back and form, rather than form data or in the request body. When the data is passed via request body some web servers will read that as a POST method, which can wreak havoc on your API.
In my case I was passing the object id both in the URI and, as a side effect of my architecture, in the request body. This works perfectly ok in all cases except DELETE. To fix the issue I simply removed the unnecessary request body and viola!
Hope this helps someone.
See this post for more detail:
http://www.spenceruresk.com/2011/11/http-delete-requests-that-include-a-body/