My webapp has Laravel as backend framework which provides a Restful API and in the fronend Angularjs is running.
I send different requests through the api and receive the responses and based on the code of response and data included, appropriate messages are shown to user.
Recently when I send requests using PUT method or POST method, when the data has problem in validation process and Laravel should respond with a 422 code in JSON format, instead I receive a text/html response with code 200. and then everything goes wrong.
This does not happen on my local machine, Only when I test the app in production environment this happens.
I also tested UnAuthorized response which is sent with 403 code, and it works flawlessly.
I tested both the automatic validation error for Laravel (as described in documentation: When using the validate method during an AJAX request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.) and also using the following method:
return response()->json(compact('errors'),422);
I should mention that I use following methods to send AJAX requests:
function save(data, url) {
return $http({
method: 'POST',
url: url,
headers: {'Content-Type': 'application/json'},
data: angular.toJson(data)
});
}
function update(data, url) {
return $http({
method: 'PUT',
url: url + data.id,
headers: {'Content-Type': 'application/json'},
data: angular.toJson(data)
});
}
needless to say I became totally confused!
UPDATE: It seems to be a problem with Laravel validation process. when the validation runs, request become erroneous. see the following piece of code:
public function altUpdate(Request $request){
$this->authorize('editCustomer', $this->store);
if (!$request->has('customer')){
return response()->json(["message"=>"Problem in received data"],422);
}
$id = $request->customer['id'];
$rules = [
'name' => 'required',
'mobile' => "required|digits:11|unique:customers,mobile,$id,id,store_id,$this->store_id",
'phone' => 'digits_between:8,11',
'email' => "email|max:255|unique:customers,email,$id,id,store_id,$this->store_id",
];
//return response()->json(["problem in data"],422); //this one works properly if uncommented
$validator = Validator::make($request->customer,$rules);
if ($validator->fails()){
$errors = $validator->errors()->all();
Log::info($errors);
return response()->json(["problem in data"],422);//this one is received in client side as a text/html response with code 200
}
$customer = Customer::find($id);
$customer->update(wrapInputs($request->all()));
if ($request->tags) {
$this->syncTags($request->tags, $customer);
}
$message = "Customer updated successfully!";
return response()->json(compact('message'));
}
I still don't know what's the problem of validation process. this code is working on my local machine without any problems but on the production server problem occurs.
I finally got that.
I had added a language file and the file was encoded in UTF-8-BOM, when I converted that file to UTF-8 without BOM things become correct.
the file was resources/lang/[the language]/validation.php and because of the encoding problem the headers were being sent while processing this file.
This question also helped me to find the problem:
Laravel redirect::route is showing a message between page loads
Related
I am building a LARAVEL PHP API in order to be consumed by Delphi 2007.
Basically, in Delphi I am performing a POST and in PHP I am validating the fields. If it fails validation, I need to return code 422 along with the validation errors (array).
In Delphi, I'm using Indy10. In it I have a Client of type TIdHTTP.
To do the POST, I do:
Client.Post(sFullEndPoint, Request, Response);
To get code 422:
Client.ResponseCode;
To get the content of the response:
Response.DataString;
In PHP, if I return only one array of errors, as return $ errors I can handle it in Delphi with Response.DataString, the problem is that I won't know the response code, because it will come 200.
If I return response ($ errors, 422) in PHP, Delphi does not find the value of $errors in response.
I need to get the HTTP code and the response body. Can someone help me?
you can set http response code in php like (refrence : https://www.php.net/manual/en/function.http-response-code.php) :
http_response_code(422);
also in laravel you can do it like :
return response('Your output string', 200)->header('Content-Type', 'text/plain');
but i suggest you to pass response as json and add some information about what is wrong or ... :
$errors = [
[
'error_code'=>1312,
'error_message'=>'name is empty'
]
];
return Response::json($errors, 201); // Status code here
I setup fresh laravel 5.2 project and in my routes.php file I have:
Route::get('/happy', function () {
abort(403,'Unauthorized - its OK :)');
});
Route::post('/sad', function () {
abort(403,'Unauthorized - its not OK :(');
});
Here are screenshots from postman:
For GET i have good response status code = 403, but for POST i get bad response status code = 200. How to make POST status codes right(=403)?
I use body-raw POST mehtod for sending json (not in this exaplme).
Instead of using raw, use form data to send post value, then you will get an exact result
May be just do what is writen on Postman response: set 'always_populate__raw_post_data' to -1 in php.ini
I am trying to send a PUT request method from my Android app to my PHP endpoint but in my endpoint the PUT request is not recognized as a PUT request so I return Request method is wrong! message from my endpoint.
Android interface and request execution
Interface for activation
#PUT("device/activate.php")
Call<DeviceRegistry> registryDevice();
Executing the request
DeviceRegistryAPI registryAPI =
RetrofitController.getRetrofit().create(DeviceRegistryAPI.class);
Call<DeviceRegistry> registryCallback = registryAPI.registryDevice();
response = registryCallback.execute();
With this I am expecting a response but I am getting my endpoint error message.
My PHP endpoint
if($_SERVER['REQUEST_METHOD'] == "PUT"){
//doing something with the data
} else {
$data = array("result" => 0, "message" => "Request method is wrong!");
}
I don't know why the $_SERVER['REQUEST_METHOD'] == "PUT" is false but I wonder if I am missing something on Retrofit 2.
More Info.
I am using Retrofit2.
Update 1: Sending json into the body
I am trying to send a json using the body.
It is my json:
{
"number": 1,
"infoList": [
{
"id": 1,
"info": "something"
},
{
"id": 2,
"info": "something"
}
]
}
There are my classes:
class DataInfo{
public int number;
public List<Info> infoList;
public DataInfo(int number, List<Info> list){
this.number = number;
this.infoList = list;
}
}
class Info{
public int id;
public String info;
}
I changed the PUT interface to this:
#PUT("device/activate.php")
Call<DeviceRegistry> registryDevice(#Body DataInfo info);
But I am getting the same problem.
Update 2: Do I need Header
I have this header in my REstfull client:
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Do I need to put this on my request configuration? How do I do that if I need it?
Update 3: checking the request type of my sending post.
Now I am checking the type of the request. Because I am having the same problem with the PUT/POST requests. So If can solved the problem with the put maybe all the problems will be solved.
When I execute the request and asking and inspect the request it is sending the the type (PUT/POST) but in the server php only detect or GET?? (the below example is using POST and the behavior is the same)
Call<UpdateResponse> requestCall = client.updateMedia(downloadItemList);
Log.i("CCC", requestCall .request().toString());
And the output is a POST:
Request{method=POST, url=http://myserver/api/v1/media/updateMedia.php, tag=null}
so I am sending a POST (no matter if I send a PUT) request to the sever but why in the server I am receiving a GET. I am locked!!! I don't know where is the problem.
Update 4: godaddy hosting.
I have my php server hosting on godaddy. Is there any problem with that? I create a local host and everything works pretty good but the same code is not working on godaddy. I did some research but I didn't find any good answer to this problem so Is possible that godaddy hosting is the problem?
PHP doesn't recognize anything other than GET and POST. the server should throw at you some kind of error like empty request.
To access PUT and other requests use
$putfp = fopen('php://input', 'r'); //will be a JSON string (provided everything got sent)
$putdata = '';
while($data = fread($putfp, filesize('php://input')))
$putdata .= $data;
fclose($putfp);
//php-like variable, if you want
$_PUT = json_decode($putdata);
did not tested, but should work.
I guess the problem is that you don't pass any data along with PUT request, that's why PHP recognizes the request as a GET. So I think you just need to try to pass some data using #FormUrlEncoded, #Multipart or probably #Body annotations
To add header in your retrofit2 you should create an interceptor:
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException
{
okhttp3.Request.Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Content-Type", "application/x-www-form-urlencoded");
ongoing.addHeader("Accept", "application/json");
return chain.proceed(ongoing.build());
}
};
and add it to your client builder:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(interceptor);
PHP recognises 'PUT' calls. Extracted from PHP.net:
'REQUEST_METHOD' Which request method was used to access the page;
i.e. 'GET', 'HEAD', 'POST', 'PUT'.
You don't need to send any header if your server isn't expecting any
header.
Prior to use Retrofit or any other networking library, you should check the endpoint using a request http builder, like Postman or Advanced Rest Client. To debug the request/response when running your app or unit tests use a proxy like Charles, it will help you a lot to watch how your request/response really looks.
I have seen a lot of questions here with the exact same problem, but none of them have a solution that works for me.
I have a NodeJS server setup and in an Angular controller I'm trying to get a contact form working. On form submission I call this function (this is only one of the many variations on a POST request I've tried):
$scope.submit_contact_form = function(){
$http({
method: "post",
url: 'http://' + window.location.host + "/form-u10657.php",
data: $scope.form_data,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function(data) {
$scope.status_message_toggle("Success");
}).error(function(){
$scope.status_message_toggle("Problem sending mail", 'warning');
});
};
But it always returns a 404 not found error. The php file is definitely in the right directory. Going to the url with a GET request works.
What is different in my setup, that I haven't seen in other questions, is that I am using a NodeJS server and ngRouteProvider.
Other answers have said to allow NodeJS to accept POST requests, but it hasn't been necessary to configure NodeJS before, and I would like to avoid it now.
Is there a way to fix it just in Angular? And if not what should I do with NodeJS to get it working?
I am not any kind of RESTful API expert, but I have a simple PUT/DELETE function in an AngularJS app that has been functioning as expected until now. I am trying to work out whether this problem is likely to lie in my app, or in the (php) back-end that is running the endpoint. Other REST services are functioning normally & the server appears to be running fine.
This function only ever calls PUT or DELETE, assigned as var method:
if (food.favourite === true) {
method = "PUT";
console.log("method is " + method)
} else if (food.favourite === false) {
method = "DELETE";
console.log("method is " + method)
}
$http({
method: method,
url: $scope.URL
}).success(function (data, status, headers, config) {
console.log(method + " successful")
}).error(function (data, status, headers, config) {
console.log(method + " not successful")
});
I have one $http GET in my app that uses a different endpoint. There is no $http GET pointing to this endpoint anywhere in my app- I have searched extensively.
When I trigger the function containing the $http above, the console shows:
method is PUT
GET http://localhost:8888/api/ext/51/ 500 (Internal Server Error)
PUT not successful
Why would I be receiving a GET error on an unsuccessful PUT request? Does this point to a problem in my function, or a problem with the endpoint?
Thank you for any help in understanding this problem.
Update 1
Info from the Network panel: calling the $http function above triggers two simultaneous requests, one 'PUT' and one 'GET'. The 'PUT' returns a 301 code, and the 'GET' returns a 500 server error (which I think is to be expected, as this endpoint is not set up to respond to 'GET', only to 'PUT' and 'DELETE').
So: why would my code be generating two simultaneous requests with different methods?
For future seekers of answers to similar questions: it is a standard behaviour (of REST in general or this implentation? Not sure) to try to return a GET for every action that is called. Evidence for this is that if I check the Network panel for all the other (successful) $http functions, they also have two actions visible: the original PUT/GET/DELETE etc, + a GET.
We are seeing a 500 Error on the GET for these particular requests because the configuration of this particular endpoint does not allow for a GET. This should not have any effect on the PUT/DELETE actions on this endpoint- the 500 Error is not related to the reason why the PUT/DELETE actions weren't working. In terms of trying to solve this specific problem, it's a red herring.
The reason the PUT/DELETE was not working was because the service was broken on the server-side.
I ran into this and the issue was in how I was outputting the errors in the first argument for header. They need to be in this format:
header('400: Error', true, 400);
or in your case, of course:
header('301: Moved Permanently', true, 301);
header('Location: ' . $url);
Note that this WILL NOT work:
header('Some Random Text', true, 400); // $http.error shows status of 500