daniel
daniel

Reputation: 35703

Post JSON from angular 2 to php

I try to post data from angular 2 to php:

let headers = new Headers();
headers.append('Content-Type', 'application/json');
var order = {'order': this.orders};

this.http.post('http://myserver/processorder.php', JSON.stringify(order), {
    headers: headers
}).subscribe(res => {
    console.log('post result %o', res);
});

In angular 2 one can only post string as data and not Json? That's ok for me but I struggle to get the posted data on php. I tried $obj = $_POST['order'];

Upvotes: 10

Views: 12545

Answers (4)

Willie
Willie

Reputation: 338

Hey if you are still reading this in 2023... I followed @Gabriel Martins answer above and that got me most of the way there... but adding 'data=' into the call was unncessary due to me choosing to use "php://input".

This project is currently in development and using Apache, php (8.1), Angular 16.

So my front end call (Using Angular 16) looks like the following:

public createPost(form: any){
    let headers = new HttpHeaders();
    headers.append('Accept', 'application/x-www-form-urlencoded');
    headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
    //method value below is 'createPost'
    let obj = JSON.stringify(Object.assign(form, {method: environment.endpoints.createPost.method}));
    return this.http.post(environment.origin + environment.endpoints.createPost.url, obj, {['headers']: headers}).pipe()
    //At this point, everything is almost the same as Gabriel's answer.
  }

And my backend code (using PDO for mysql) and a small custom library is:

<?php

namespace System;

class Request {
    
    private $headers = [];
    private $method = [];
    private $inputs = [];
    private $files = [];
    
    public static $instance;
    
    public function __construct() {
        self::$instance = $this;
        $this->headers = apache_request_headers();
        $this->method = strtolower($_SERVER['REQUEST_METHOD']);
        
        if($this->method == 'post'){
            //This is one spot where our answers are different. 
            //The addition of 'data=' causes unneeded parsing here. 
            //Just passing a stringified JSON seemed to work better. 
            //Note the second argument for true to json_decode.
            $input = json_decode(file_get_contents("php://input"), true);
            if(isset($this->headers['Content-Type'])){
                $content_type = $this->headers['Content-Type'];

                if($content_type == 'application/x-www-form-urlencoded' || strstr($content_type, 'text/plain')){
                    foreach($input as $key=>$value){
                        $this->inputs[$key] = filter_var($value, FILTER_DEFAULT);
                    }
                }
            }
        }
    }
    /**
     * returns this class instance
     * @return self
     */
    public static function get(){
        if(self::$instance === null){
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * returns the method used to access the current request
     * @return string
     */
    public function get_method() : string{
        return $this->method;
    }
    
    /**
     * returns all the headers in the current request
     * @return array
     */
    public function get_headers() : array{
        return $this->headers;
    }
    
    /**
     * returns the header or null by key
     * @param string $key
     * @return type
     */
    public function get_header(string $key){
        return $this->headers[$key] ?? null;
    }
    
    /**
     * returns the input value in the request or null
     * @param string $var
     * @return type
     */
    public function get_input(string $var){
        return $this->inputs[$var] ?? null;
    }
    
    /**
     * returns all the input fields in the current request
     * @return array
     */
    public function get_all_inputs(): array{
        return $this->inputs;
    }
    
    public function get_files(): array{
        return $this->files;
    }
}
?>

The public API code that actually calls my PDO library is:

<?php
    require_once(__DIR__."/../Models/BlogPosts.php");
    require_once(__DIR__."/../System/Request.php");
    
    $blogModel = new \Models\BlogPost();
    $req = \System\Request::get();
    
    switch($req->get_input('method')){
        case 'createPost':
            print_r($blogModel->createPost(
                $req->get_input('url'),
                $req->get_input('title'),
                $req->get_input('description'),
                $req->get_input('author'),
                $req->get_input('category'),
                $req->get_input('sequence'),
                $req->get_input('image'),
                $req->get_input('imageWebp'),
                $req->get_input('content')
            ));
            break;
        case 'getFullPostByID':
            print_r($blogModel->getFullPostByID($req->get_input('id')));
            break;
        case 'getAllPosts':
            print_r($blogModel->getAllBasicPostInfo());
            break;
    }

?>

Anyway, I hope that helps someone.. I also made sure to append a slash at the end of the request URL, to send the request to 'api/posts.php/' instead of 'api/posts.php'

Upvotes: 0

Gabriel Martins
Gabriel Martins

Reputation: 662

I do not know if it's bad practice but it seems right to me, although it bothers me a little

const headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

const obj = { nome: 'gabriel', age: 20 };
const body = 'data=' + JSON.stringify(obj);

this.http.post('/test.php', body, { headers })
  .subscribe(res => console.log(res.json()), res => console.error(res))

And in php

$post = json_decode($_POST['data']);

Upvotes: 5

Thierry Templier
Thierry Templier

Reputation: 202176

Agreed with you that we can't at the moment provide object instead of string. It's a feature in progress. See this issue:

Regarding your problem to get JSON data on the server side, this question should help you:

Upvotes: 2

inki
inki

Reputation: 1946

Marc B is correct, however what is happening is that the $_POST array will contain an empty value with a key set to the JSON string you are passing...

Array
(
    [{"order":"foobar"}] => 
)

You "can" grab that (although this would be the wrong approach) by getting the key using...

key($_POST)

for example:

$obj = json_decode(key($_POST));
echo $obj->order;

BUT what you can do is send the data as value key pairs:

let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
let order = 'order=foobar';

this.http.post('http://myserver/processorder.php', order, {
    headers: headers
}).subscribe(res => {
    console.log('post result %o', res);
});

Then in PHP you can grab the data using:

$_POST['order']

Few things to note:

  • changed header Content-Type to application/x-www-form-urlencoded (more for my own testing since this doesn't do any preflight requests)
  • notice that order is a key value pair string instead of JSON
  • notice that order in this.http.post is getting passed as-is without JSON.stringify

Upvotes: 9

Related Questions