1

I'm using HttpClient to POST data from Angular 7 to PHP server. A reactive form takes the necessary data from the user and then posts it to an url with the following:

admin.component.ts:

constructor(private postMedicineService: MedicineService) { }

 ngOnInit() {
    this.medicineInfo = new FormGroup({
      name: new FormControl(null, Validators.required),
      description: new FormControl(null, Validators.required),
      category: new FormControl(null, Validators.required),
      image: new FormControl(null, Validators.required),
      price: new FormControl(null, Validators.required)
    });
}

  submitMedicineInfo() {
    this.postMedicineService.postMedicine(this.medicineInfo.value)
      .subscribe(
        (response) => console.log(response),
        (error) => console.log(error)
      );
  }

medicine.service.ts:

const postUrl = 'http://localhost/petrapharm-backend/create.php';

@Injectable()
export class MedicineService {

    constructor(private httpClient: HttpClient) { }

    postMedicine(medicine: Medicine) {
        console.log(medicine);
        return this.httpClient.post(postUrl, medicine);
    }
}

medicine-object.service.ts:

export interface Medicine {
    name: String;
    description: String;
    category: String;
    image: File;
    price: number;
}

And finally I have the PHP server that takes the POST request from the Angular 7 app with the following:

create.php:

<?php
// required headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// get database connection
include_once './database.php';

// instantiate product object
include_once './medicine.php';

$database = new Database();
$db = $database->getConnection();

$product = new Medicine($db);

// get posted data
$data = json_decode(file_get_contents("php://input"));


// make sure data is not empty
if(
    !empty($data->name) &&
    !empty($data->description) &&
    !empty($data->category) &&
    !empty($data->image) &&
    !empty($data->price)
){

    // set product property values
    $product->name = $data->name;
    $product->description = $data->description;
    $product->category = $data->category;
    $product->image = $data->image;
    $product->price = $data->price;

    // create the product
    if($product->create()){

        // set response code - 201 created
        http_response_code(201);

        // tell the user
        echo json_encode(array("message" => "Product was created."));
    }

    // if unable to create the product, tell the user
    else{

        // set response code - 503 service unavailable
        http_response_code(503);

        // tell the user
        echo json_encode(array("message" => "Unable to create product."));
    }
}

// tell the user data is incomplete
else{

    // set response code - 400 bad request
    http_response_code(400);

    // tell the user
    echo json_encode(array("message" => "Unable to create product. Data is incomplete."));
}
?>

medicine.php:

<?php
class Medicine{

    // database connection and table name
    private $conn;
    private $table_name = "medicine";

    // object properties
    public $id;
    public $name;
    public $description;
    public $category;
    public $image;
    public $price;

    // constructor with $db as database connection
    public function __construct($db){
        $this->conn = $db;
    }

    // create product
    function create(){

        // query to insert record
        $query = "INSERT INTO
                " . $this->table_name . "
            SET
                name=:name, description=:description, category=:category, image=:image, price=:price";

        // prepare query
        $stmt = $this->conn->prepare($query);

        // sanitize
        $this->name=htmlspecialchars(strip_tags($this->name));
        $this->description=htmlspecialchars(strip_tags($this->description));
        $this->category=htmlspecialchars(strip_tags($this->category));
        $this->image=htmlspecialchars(strip_tags($this->image));
        $this->price=htmlspecialchars(strip_tags($this->price));

        // bind values
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":category_id", $this->category);
        $stmt->bindParam(":created", $this->image);
        $stmt->bindParam(":price", $this->price);

        // execute query
        if($stmt->execute()){
            return true;
        }

        return false;

    }
}

The error it gives me in chrome:

zone.js:3243 OPTIONS http://localhost/petrapharm-backend/create.php 400 (Bad Request)

Access to XMLHttpRequest at 'http://localhost/petrapharm-backend/create.php' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

I also tried running the POST request in Insomnia and Postman, but they return the following reposonse

{
    "message": "Unable to create product. Data is incomplete."
}
Milan Panic
  • 473
  • 1
  • 9
  • 30
  • Search about XSS and CORS. You api server must reply to an OPTIONS call with some special headers that tells browser if it can or cannot do that request. It's an security pattern on most browsers, and you must follow it. – Elias Soares Apr 21 '19 at 01:01
  • 1
    Also, reading you code, I suggest you to use FormBuilder class, it will make your form creation easier and cleaner with no need to instantiate so many form controls and form groups. – Elias Soares Apr 21 '19 at 01:03
  • Thank you for your answer, but it is not working still, the PostMan request doesn't work either, and the doesn't need POST. Thank you for the suggestion, I will look into the FormBuilder class :) – Milan Panic Apr 22 '19 at 11:18

1 Answers1

1

This is due to the browser's CORS policy. Browsers can interpret different ports as different origins, since your angular code is served on 4200 and localhost is likely on something like 8100, you will see this error within the browser.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

For a workaround you can try to disable CORS within the Chrome browser, if you are on a Mac this is ran from the terminal:

open -a Google\ Chrome --args --disable-web-security --user-data-dir=""

For windows this answer covers a solution: Disable same origin policy in Chrome

Jay Ordway
  • 1,708
  • 2
  • 11
  • 33