CORS Issue - Angular / PHP / Apache - javascript

I think that there are plenty of similar topics here and there on the internet, but I did just spend 1h searching and still can't fix this.
I cannot make a request with POST on my server (Apache & PHP) with Angular.
I use angular/cli v.6.2.1 with node 10, apache 2.4 & php 7.1
Here is a simple code from the Http call (HttpClient & HttpHeaders both come from #angular/common/http) :
constructor(private http: HttpClient){}
this.http.post('http://localhost/distributor.php', ['prop1':'value1', 'prop2':'value2'], {headers: new HttpHeaders().set('Access-Control-Allow-Origin','*')}).subscribe(
data => {
console.log(data);
},
err => {
console.log('error');
});
}
I just try to send something back from PHP this way :
<?php
$data = $_POST;
echo json_encode($data);
I already allowed all origins in apache configuration file.
Both Firefox & Chrome just let me down after a "OPTIONS" preflight and do not do anything else, no return from the PHP file.
Here is what FireFox shows me :
and I can see this in the network tab :
Response tab shows a completely empty box.
I can remove the custom header's part from my http.post it changes nothing.
What seems strange to me is that I can click the FireFox edit & resend button, without changing nothing, and the right results appear...
Thanks for reading/help

First you need to fix your POST data; you have square brackets around Object syntax.
const data = { 'prop1': 'value1', 'prop2': 'value2' };
this.http.post('http://localhost/distributor.php', data).subscribe(
reply => {
console.log(reply);
},
err => {
console.log('error', err);
});
Next, you need to add proper headers and set PHP up to deal with JSON:
<?php
header('Access-Control-Allow-Headers: Access-Control-Allow-Origin, Content-Type');
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json, charset=utf-8');
// grab JSON data sent by Angular
$data = json_decode(file_get_contents('php://input'), true);
// add numeric data
$data["prop3"] = 3;
// reply
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
This worked for me.

I had the same issue but it resolved by adding these lines to your php code
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Content-Type: application/json');
let data = {
'prop1': 'value1',
'prop2': 'value2'
};
return this.http.post('http://localhost/distributor.php', data, {headers: new HttpHeaders().set('Access-Control-Allow-Origin', '*')}).map((response: Response) => {
let data = response;
if (data) {
console.log(data);
}
})

Related

Logging out with Servant (Haskell)

I've been trying to implement a simple server with cookie-based authentication using Servant.
I found an example here.
Server
I created my API:
type API
= Auth '[Cookie] User :>
"login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
, Header "Set-Cookie" SetCookie ] NoContent)
:<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
, Header "Set-Cookie" SetCookie ] NoContent)
Here's an implementation for the endpoints:
checkCreds :: CookieSettings
-> JWTSettings
-> Credentials -- my type storing user's login and pass
-> Handler (Headers '[Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie] NoContent)
checkCreds cookieSettings jwtSettings Credentials { credentialsUserName = userName, credentialsPassword = pass} = do
authRes <- checkIfUserExists userName pass
case authRes of
Just (name, key) -> do
mApplyCookies <- liftIO $ acceptLogin cookieSettings jwtSettings (Session key name)
return $
case mApplyCookies of
Nothing -> clearSession cookieSettings NoContent
Just applyCookies -> applyCookies NoContent
Nothing ->
throwError err401
getLogout :: CookieSettings
-> Handler (Headers '[ Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie ] NoContent)
getLogout cookieSettings = return $ clearSession cookieSettings NoContent
The cookieSettings I use are here:
cookieSettings :: CookieSettings
cookieSettings = defaultCookieSettings {
cookieIsSecure = NotSecure,
cookieSameSite = SameSiteStrict,
sessionCookieName = "MyCookie",
cookieXsrfSetting = Just def {xsrfExcludeGet = True}
}
Client
I use JavaScript fetch to poke the login endpoint:
let opts: RequestInit = {
method: "POST",
headers: new Headers({ "Content-Type": "application/json" }),
credentials: "include",
body: json,
};
fetch("http://localhost:8081/login", opts)
.then((value) => {
// Do something
}
);
This works fine and I noticed the cookies are included in the response and I can find them in Storage -> Cookies in my Firefox.
Then I use a similar method to poke the logoutendpoint:
const sendLogOut = async () => {
let resp = await fetch(Urls.logout.href, { method: "GET" });
console.log(resp.status);
};
it prints 200 in my console and I can see the cookies are included in the response:
However, nothing else happens. It seems that the response gets discarded and the cookies I had received from login are still valid.
Questions
1.) How shall I implement the logout feature properly.
2.) As I'm relatively new to web development, where can I find useful information about HTTP protocol? By "useful" I meant "something that shows examples instead of raw definitions".
I've got the answer.
In my case the problem was caused by the fact that my servant app and my JS client were technically two separate applications, hosted on two different ports (8081 and 8833 respectively).
When I set up nginx and configured routing under a single domain, everything works as expected.

JSON Decode not receiving data

I am trying to use the Stripe API to create a payment form as detailed here:
https://stripe.com/docs/payments/integration-builder
I would like to send the amout (that the user is charged) from the front-end so have attempted to add it to the fetch request as shown below:
var purchase = {
//items: [{ id: "xl-tshirt", price: 400 }]
amount: 2000
};
// Disable the button until we have Stripe set up on the page
document.querySelector("button").disabled = true;
fetch("/create.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(purchase)
});
However the value (currently hardcoded at 2000) is not pulling through to the POST body successfully and the payment intent is failing. Below is the code I am using:
try {
// retrieve JSON from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str, false);
$paymentIntent = \Stripe\PaymentIntent::create([
//'amount' => calculateOrderAmount($json_obj->items),
'amount' => $json_obj['amount'],
'currency' => 'usd',
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
Any advice is much appreciated.
You are sending false here, that converts it into an object
$json_obj = json_decode($json_str, false);
Then you are trying to use it as an array here
'amount' => $json_obj['amount'],
Try using
'amount' => $json_obj->amount,
Or
$json_obj = json_decode($json_str, true);
without changing anything else.

how to fix 'Access to XMLHttpRequest has been blocked by CORS policy' Redirect is not allowed for a preflight request only one route

i'm setting a laravel and vuejs.
CORS plugin for laravel and frontend side i use Axios to call REST api
i got this ERROR
Access to XMLHttpRequest at 'https://xx.xxxx.xx' from origin
'http://localhost:8080' has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: Redirect is not allowed for a
preflight request.
this is for a vuejs axios setup **main.js**
axios.defaults.baseURL = process.env.BASE_URL;
axios.defaults.headers.get['Accepts'] = 'application/json';
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
axios.defaults.headers.common['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept';
**content.vue file**
this.loading = true;
var companyId = this.$route.params.cid;
var userId = this.$route.params.uid;
const thisVue = this;
var formData = new FormData();
let data = {};
formData.append("subject", this.title);
formData.append("content", this.content);
formData.append("posting_article_url", this.blog_link);
formData.append("recruitment_tension", this.sel_recruitment_tension);
formData.append("why_hire_engineer", this.sel_company_hire_engineer);
formData.append("technique_skill", this.requiredTechniqueSkill);
formData.append("better_technique_skill",this.betterTechniqueSkillIfThereIs);
formData.append("personality", this.requiredPersonality);
formData.append("any_request", this.anyRequest);
formData.append("location", this.location);
formData.append("supplement_time", this.supplement_time);
formData.append("supplement_contract", this.supplement_contract);
formData.append("en_benefits", this.en_benefits);
formData.append("recruit_role", this.recruit_role);
formData.append("how_to_proceed", this.how_to_proceed);
formData.append("current_structure", this.current_structure);
if (this.selectedSkill.length > 0)
{
let selectedSkills = this.selectedSkill
.filter(obj => {
return obj.id;
})
.map(item => {
return item.id;
});
formData.append("skill_keyword", selectedSkills);
}
if (this.imageBlob != "") {
formData.append("image", this.imageBlob, "temp.png");
}
for (var i = 0; i < this.sel_schedule.length; i++) {
formData.append("freelance_type[" + i + "]", this.sel_schedule[i])
}
for (var i = 0; i < this.sel_type_of_contract.length; i++) {
formData.append("contract_type[" + i + "]", this.sel_type_of_contract[i])
}
this.loading = false;
$('html, body').animate({scrollTop:300}, 'slow');
} else {
axios
.post(
"/xx/xxx/?token=" + localStorage.getItem("token"),
formData,
{
headers: [
{ "X-localization": localStorage.getItem("lan") },
{ "Access-Control-Allow-Origin": '*' },
{ "Access-Control-Allow-Headers": 'Origin, X-Requested-With, Content-Type, Accept '},
{ "Access-Control-Allow-Methods": "POST, GET, PUT, OPTIONS, DELETE" },
{ "Access-Control-Max-Age": 3600 }
]
}
)
.then(res => {
if (!res.data.result) {
if (res.data[0]) {
this.$toaster.error(res.data[0]);
this.$store.dispatch("logout");
}
if (res.data.errors) {
for (var i = 0; i < res.data.errors.length; i++) {
this.$toaster.error(res.data.errors[i].message);
}
}
this.loading = false;
} else {
this.$toaster.success(thisVue.$t("success_recruit_add"));
}
})
.catch(() => {
this.$toaster.error(thisVue.$t("err_network"));
});
}
the error occur only one route rest all are working.
also working on Postman
Permanent solution from server side:
The best and secure solution is to allow access control from server end. For laravel you can follow the following steps:
In App\Http\Middleware\Cors.php:
public function handle($request, Closure $next)
{
return $next($request)->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods','GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS');
}
In App\Http\Kernel.php:
protected $middleware = [
...
\App\Http\Middleware\Cors::class,
];
Temporary solution from browser side:
If you want to disable CORS from browser-end then follow one of the following steps:
Safari: Enable the develop menu from Preferences > Advanced. Then select “Disable Cross-Origin Restrictions” from the develop menu.
Chrome (Extension): Use the Chrome extension Allow CORS: Access-Control-Allow-Origin
Chrome (CMD): Close all your Chrome browser and services. Then run the following command:
Windows:
“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” –-allow-file-access-from-files --disable-web-security --user-data-dir --disable-features=CrossSiteDocumentBlockingIfIsolating
Mac:
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome — args — user-data-dir=”/tmp/chrome_dev_test” — disable-web-security
The problem comes from your Vue App.
Eg: You're requesting the url below:
https://example.com/api/methods/
And the backend redirect it to:
https://example.com/api/methods
Beware of the trailing slash at the end.
The issue is from the back-end side in our case is Laravel, in your config/cors.php try to use the below config:
'supportsCredentials' => true,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'],
'exposedHeaders' => [],
'maxAge' => 0,
Or you can try to use this code in the top of public/index.php
Edit
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-
Request-With');
The problem is from the server side. If you are using express js. Try to install the express cors package on your server.
npm install cors
In your app.js require cors.
var cors = require('cors')
Then, add it as a middleware to your app.
app.use(cors())
You should not experience the cors issue after installing the package.
We can fix with APP_URL, if you use it as the base url for axios request. Please, make sure your browser root url and APP_URL in .env both are same.
For example, if you run the app on "http://127.0.0.1:8000" then should be the APP_URL=http://127.0.0.1:8000
And if you run the app on "http://localhost:8000" then should be the APP_URL=http://localhost:8000
Hope, this will help! And it's tested with laravel6.x
The cors (Cross-Origin Resource Sharing) handle by server side. If you are come from laravel end so the barryvdh/laravel-cors package is help to solve this error
url:
https://packagist.org/packages/barryvdh/laravel-cors
You probably have some misconfiguration either on the webserver side or Laravel side. Perhaps this solution might help you: Why isn't my nginx web server handling ttf fonts?.
Pay close attention to the OPTIONS method, since this enables the support for Preflight.
Disabling this flag worked for me:
chrome://flags/#block-insecure-private-network-requests
nelmio_cors:
defaults:
allow_origin: ["*"]
allow_headers: ["*"]
allow_methods: ["POST", "PUT", "GET", "DELETE", "OPTIONS"]
max_age: 3600
origin_regex: false
paths:
'^/': ~
add in nelmio_cors /packge/nelmio_cors
Steps
Go to this link
https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
download it
switch on the chrome web browser extension
Check your http link
example http to https of the remote url.
do the get api.

Error opening a Salesforce File after creating a content version using REST API

I am using typescript (angular 4) to post to the salesforce endpoint
https://yourInstance.salesforce.com/services/data/v41.0/sobjects/ContentVersion/
My http request looks like the following
Request Headers
Content-Type: multipart/form-data; boundary="1524931327799"
Authorization: Bearer <token>
Request Body
--1524931327799
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json; charset=UTF-8
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg"
}
--1524931327799
Content-Type: image/jpeg
Content-Disposition: form-data; name="VersionData"; filename="IMG_0400.jpg"
/9j/4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA <rest of base64 data>
--1524931327799--
After opening the image on the salesforce platform I get an error that the image may be damaged or use a file format that Preview doesn’t recognize. When I open the image using text edit there is the identical base64 data that is sent in the request. It seems the problem comes with salesforce not recognizing that the file is an image and does not decode the base64 data. All and any help is welcomed! Thank you.
I just tried it using developer workbench and request like this should work just fine. Try not to define 2 different content types in the request, rather put definition of file into the VersionData attribute of ContentVersion Object
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg",
"VersionData" : "4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA"
}
I was never able to post to the /ContentVersion/ endpoint. After doing some research the simplest solution I found was to use jsforce https://jsforce.github.io/.
Solution using jsforce:
1. Import jsforce library in your index.html "https://cdnjs.cloudflare.com/ajax/libs/jsforce/1.7.0/jsforce.min.js"
2. Import jsforce at the top of your angular component
declare var jsforce:any;
Start a connection with jsforce
var conn = new jsforce.Connection({
loginUrl: "https://test.salesforce.com/",
clientId : "",
clientSecret : "",
redirectUri : ""
});
3. Login to SF and post to composite endpoint using requestPost
var username = "";
var password = "";
conn.login(username, password, function(err, userInfo) {
if (err) { return console.error(err); }
var path = '/services/data/v41.0';
return conn.requestPost( path + '/composite/', {
'allOrNone' : true,
'compositeRequest' : [
{
'method' : 'POST',
'url' : path + '/sobjects/ContentVersion/',
'referenceId' : 'newFile',
'body' : {
'Title' : fileToPost.name,
'PathOnClient' : fileToPost.name,
'VersionData' : base64FileData
}
}
]
})

jQuery AJAX to upload picture => PHP sends back responseText with HTML instead of JSON

My goal is to upload an image to PHP SYMFONY through jQuery AJAX. Thanks to the input I got on some other SO thread, I manage to get the file going to the PHP side. Now the answer I expect back from PHP to AJAX is a JSON.
I can see my PHP creates the JSON and sends it back, but for some reasons (that I am suspecting being a PHP.INI setting) the AJAX Object has in its responseText some HTML at the beginning of it:
"<br /> <b>Notice</b>: Unknown: file created in the system's temporary directory in <b>Unknown</b> on line <b>0</b><br /> "{\u0022status\u0022:\u0022success\u0022,\u0022fileUploaded\u0022:true}""
My code on AJAX side looks like that:
var pictureFormData = new FormData();
pictureFormData.append('pictureFile', $(#[input_type_file_on_DOM_id])[0].files[0]);
$.ajax({
url: [my php to integrate image].php,
data: pictureFormData,
type:"post",
contentType:false,
processData:false,
cache:false,
dataType:"json",
success:function(res){
console.log('AJAX SUCCESS');
console.info(res);
},
error:function(err){
console.log('AJAX ERR');
console.info(err);
},
timeout:function(){
console.log('AJAX TIMEOUT');
},
complete: function(){
console.log('AJAX COMPLETE');
}
});
On my PHP SYMFONY side the CONTROLLER dealing with the AJAX call looks like this:
class AjaxImageController extends Controller
{
public function indexAction(Request $request)
{
$file = $request->files->get('pictureFile');
$status = array('status' => "success","fileUploaded" => false);
// If a file was uploaded
if(!is_null($file)){
// generate a random name for the file but keep the extension
$filename = uniqid().".".$file->getClientOriginalExtension();
$path = "/tmp";
$file->move($path,$filename); // move the file to a path
$status = array('status' => "success","fileUploaded" => true);
}
$data = json_encode($status);
$response = new JsonResponse();
$response->setData($data);
dump($response);
return $response;
}
}
As you can see I do a dump(response) just before sending it back. Its content is as follow:
JsonResponse {#719 ▼
#data: ""{\u0022status\u0022:\u0022success\u0022,\u0022fileUploaded\u0022:true}""
#callback: null
#encodingOptions: 15
+headers: ResponseHeaderBag {#722 ▼
#computedCacheControl: array:2 [▼
"no-cache" => true
"private" => true
]
#cookies: []
#headerNames: array:2 [▼
"cache-control" => "Cache-Control"
"content-type" => "Content-Type"
]
#headers: array:2 [▼
"cache-control" => array:1 [▼
0 => "no-cache, private"
]
"content-type" => array:1 [▼
0 => "application/json"
]
]
#cacheControl: []
}
#content: ""{\u0022status\u0022:\u0022success\u0022,\u0022fileUploaded\u0022:true}""
#version: "1.0"
#statusCode: 200
#statusText: "OK"
#charset: null
}
Hence I don't get why the responseText is erroneous containing HTML at its begining with: "<br /> <b>Notice</b>: Unknown: file created in the system's
temporary directory in <b>Unknown</b> on line <b>0</b><br />" and triggering then the error function of the AJAX call.
SOME UPDATE:
I notice the following:
On client side when I have pictureFormData.append('pictureFile', $(#[input_type_file_on_DOM_id])[0].files[0]); that is embedded in what I send to SYMFONY PHP Server: I notice in CONTROLLER incoming $request that the file appears in a FileBag Object:
Request {#9 ▼
+attributes: ParameterBag {#12 ▶}
+request: ParameterBag {#10 ▶}
+query: ParameterBag {#11 ▶}
+server: ServerBag {#16 ▶}
+files: FileBag {#14 ▼
#parameters: array:1 [▼
"pictureFile" => UploadedFile {#15 ▼
.... all files data
}
]
}
...
}
AND THEN I GET THE ERROR I DESCRIBED.
If I take away the file sent from the client side, and check the CONTROLLER, the $request looks like this:
Request {#9 ▼
... +files: FileBag {#14 ▼
#parameters: []
}
...
}
In that case the error does not show up. I suspect hence an operation on the FileBag object to throw an echo somehow.
Your response is containing HTML because php throw an error. If you correct it, then the reponse will be good.
It seem like the file doesn't exist.
If you don't see the file on your server, or in your XHR request then you need to add enctype="multipart/form-data" on your form.
If the form and XHR request are OK, then try upload your file with adding root directory to your upload directory.
# app/config/config.yml
# ...
parameters:
brochures_directory: '%kernel.root_dir%/../web/uploads/brochures'
and
$file->move(
$this->getParameter('brochures_directory'),
$fileName
);
or just in your controller
$root = $this->get('kernel')->getRootDir();
$path = $root."/tmp";
$file->move($path,$filename); // move the file to a path
The problem was due to PHP.INI settings:
the folder:
upload_tmp_dir = [path to php]\php7\uploadtemp was set in PHP.INI.
"[path to php]\php7\" existed but not folder "uploadtemp" and it couldn't create it.
I created the folder "uploadtemp" in "[path to php]\php7\" and it solved it.

Categories