Anuglar 6 .post .subscribe两次插入记录

时间:2018-07-07 03:38:03

标签: php mysql angular api

我正在尝试将Angular 6 CRUD放在一起。我的addCoins方法似乎将记录添加了两次。

addCoin(name, price) {
const obj = {
  name: name,
  price: price
};

const uri = 'http://localhost/ng6crud/api/post-coins/' + name + '/' + price;

this
  .http
  .post(uri, obj)
  .subscribe(res =>
    console.log('Done'));
}

我创建了一个简单的PHP api,位于ng6crud目录中。以下是api / post-coins /的代码-如果我使用postman张贴到API,那么它只会插入一次数据。

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
$elements = explode('/', $path);                // Split path on slashes

global $name, $price;

if(!empty($elements[3]))
{
  $name = $elements[3];
}

if(!empty($elements[4]))
{
  $price = $elements[4];
}

global $server;
$server = "localhost";
global $user;
$user = "someU";
global $pwd;
// $pwd = "someP"; // production
 $pwd = "someP"; // local
global $db;
$db = "someDb";

 //open connection to mysql db
$connection = mysqli_connect($server,$user,$pwd,$db) or die("Error " . mysqli_error($connection));

//fetch table rows from mysql db
$sql = "INSERT into coins (name, price) VALUES('" . $name . "','" . $price . "')";

// echo $sql;
// die();

if ($connection->query($sql) === TRUE) {
   // echo "Record updated successfully";
} else {
   // echo "Error updating record: " . $connection->error;
}

$connection->close();
?>

.htaccess文件和httpd-vhosts.conf文件中的“”标记的组合允许api / post-coins /接受sql插入中包括的“ name”和“ price”两个参数语句-例如api / post-coins / rupple / 1.00会在数据库中插入一行。

以下是.htaccess文件:

    IndexIgnore * # prevent directory listing

Order deny,allow
Allow from *

# ------------------------------------------
# Rewrite so that php extentions are not shown
RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^.*$ index.php%{REQUEST_URI} [QSA,L]

以下是httpd-vhosts.conf文件中“”标记内的Location标记:

    <Location /post-coins>
        ForceType appllication/x-http-php
    </Location>

再一次,如果我使用邮差发布到api / post-coins / rupple / 1.00,则数据库中将只有一个条目。在Angular 6中使用该服务时,它将两次插入数据。我尝试删除.subscribe,但是没有插入任何内容,并且尝试删除URL末尾的两个参数,但是结果是两个空条目。

预先感谢

要回答@Vikas有关如何实现的问题,以下是create.component.html:

<div class="panel panel-primary">
  <div class="panel-heading">
    {{ title }}
  </div>
  <div class="panel-body">
    <form [formGroup]="angForm" novalidate>
      <div class="form-group">
        <label class="col-md-4">Coin Name</label>
        <input type="text" class="form-control" formControlName="name" #name />
      </div>
      <div *ngIf="angForm.controls['name'].invalid && (angForm.controls['name'].dirty || angForm.controls['name'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['name'].errors.required">
          Name is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Coin Price</label>
        <input type="text" class="form-control" formControlName="price" #price/>
      </div>
      <div *ngIf="angForm.controls['price'].invalid && (angForm.controls['price'].dirty || angForm.controls['price'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['price'].errors.required">
          Price is required.
        </div>
      </div>
      <div class="form-group">
        <button (click)="addCoin(name.value, price.value)" [disabled]="angForm.pristine || angForm.invalid" class="btn btn-primary">Add</button>
      </div>
    </form>
  </div>
</div>

creat.component.ts:

    import { Component, OnInit } from '@angular/core';
import { CoinService } from '../../service/coin.service';
import { FormGroup,  FormBuilder,  Validators } from '@angular/forms';

@Component({
  selector: 'app-create',
  templateUrl: './create.component.html',
  styleUrls: ['./create.component.css']
})
export class CreateComponent implements OnInit {

  title = 'Add Coin';
  angForm: FormGroup;
  constructor(private coinservice: CoinService, private fb: FormBuilder) {
    this.createForm();
  }
  createForm() {
    this.angForm = this.fb.group({
      name: ['', Validators.required ],
      price: ['', Validators.required ]
    });
  }
  addCoin(name, price) {
    this.coinservice.addCoin(name, price);
  }
  ngOnInit() {
  }
}

还有coin.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class CoinService {

  result: any;
  constructor(private http: HttpClient) {}

  addCoin(name, price) {
    const obj = {
      name: name,
      price: price
    };

    const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

    this
      .http
      .post(uri, obj)
      .subscribe(res =>
        console.log('Done'));
  }

  getCoins() {
    const uri = 'http://localhost/ng5crud/api/get-coins/';
    return this
              .http
              .get(uri);
  }

  editCoin(id) {
    const uri = 'http://localhost/ng5crud/api/get-coins-id/' + id;
    return this
              .http
              .get(uri);
  }

  updateCoin(name, price, id) {
    const uri = 'http://localhost/ng5crud/api/put-coins/' + id;

    const obj = {
      name: name,
      price: price
    };
    this
      .http
      .post(uri, obj)
      .subscribe(res => console.log('Done'));
  }

  deleteCoin(id) {
    const uri = 'http://localhost/ng5crud/api/delete-coins/' + id;

    return this
              .http
              .get(uri);
  }
}

和routerConfig.ts

    import { Routes } from '@angular/router';
import { CreateComponent } from './components/create/create.component';
import { EditComponent } from './components/edit/edit.component';
import { IndexComponent } from './components/index/index.component';
import { DeleteComponent } from './components/delete/delete.component';

export const appRoutes: Routes = [
  { path: 'create',
    component: CreateComponent
  },
  {
    path: 'edit/:id',
    component: EditComponent
  },
  { path: 'index',
    component: IndexComponent
  },
  { path: 'delete/:id',
    component: DeleteComponent
  }
];

@Debojyoti,我刚刚看到了您的回复。我最终创建了以下内容,但没有返回任何内容,但是我在网络面板中进行了查看-请参见下面的屏幕截图:

import { HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
import { catchError} from 'rxjs/operators';

addCoin(name, price) {
  const obj = {
    name: name,
    price: price
  };

  const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

  this
    .http
    .post(uri, obj)
    .pipe(
      catchError(this.handleError) // then handle the error
    )
    .subscribe(res =>
      console.log('Done'));
}

private handleError(error: HttpErrorResponse) {
  if (error.error instanceof ErrorEvent) {
    // A client-side or network error occurred. Handle it accordingly.
    console.error('An error occurred:', error.error.message);
  } else {
    // The backend returned an unsuccessful response code.
    // The response body may contain clues as to what went wrong,
    console.error(
      `Backend returned code ${error.status}, ` +
      `body was: ${error.error}`);
  }
  // return an observable with a user-facing error message
  return throwError(
    'Something happened; please try again later.');
};

See attached screen shot

3 个答案:

答案 0 :(得分:0)

看起来PHP应用程序和Angular应用程序在协议(http / https),域和端口的单独组合上运行。我看到CORS标头已添加到PHP代码中。

这使我相信正在发出2个请求,一个可以查看是否根据CORS(一个OPTIONS)请求和正常的POST请求而允许该请求。因此,该记录被插入两次。您可以检测到请求方法并返回CORS标头,并在CORS OPTIONS请求上适当地将响应主体设置为空,并在POST请求上进行插入,类似于以下内容(未经测试)。

<?php
if ($_SERVER['method'] === 'OPTIONS') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
    header("HTTP/1.1 200 OK");

    exit;
}

if ($_SERVER['method'] === 'POST') {
    header("HTTP/1.1 200 OK");

    $path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
    $elements = explode('/', $path);                // Split path on slashes

    global $name, $price;

    if (!empty($elements[3])) {
        $name = $elements[3];
    }

    if (!empty($elements[4])) {
        $price = $elements[4];
    }

    global $server;
    $server = "localhost";
    global $user;
    $user = "someU";
    global $pwd;
    // $pwd = "someP"; // production
    $pwd = "someP"; // local
    global $db;
    $db = "someDb";

    //open connection to mysql db
    $connection = mysqli_connect($server, $user, $pwd, $db) or die("Error " . mysqli_error($connection));

    //fetch table rows from mysql db
    $sql = "INSERT into coins (name, price) VALUES('" . $name . "','" . $price . "')";

    // echo $sql;
    // die();

    if ($connection->query($sql) === true) {
        // echo "Record updated successfully";
    } else {
        // echo "Error updating record: " . $connection->error;
    }

    $connection->close();

    exit;
}

header("HTTP/1.1 404 Not Found");
exit;

?>

答案 1 :(得分:0)

让php不接受第一个飞行前请求(选项)

只需像修改您的php代码一样

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');

if ($_SERVER['REQUEST_METHOD'] != "OPTIONS") {
    $path = ltrim($_SERVER['REQUEST_URI'], '/'); // Trim leading slash(es)
    $elements = explode('/', $path); // Split path on slashes

    global $name, $price;

    if (!empty($elements[3])) {
        $name = $elements[3];
    }

    if (!empty($elements[4])) {
        $price = $elements[4];
    }

    global $server;
    $server = "localhost";
    global $user;
    $user = "someU";
    global $pwd;
    // $pwd = "someP"; // production
    $pwd = "someP"; // local
    global $db;
    $db = "someDb";

    //open connection to mysql db
    $connection = mysqli_connect($server, $user, $pwd, $db) or die("Error " . mysqli_error($connection));

    //fetch table rows from mysql db
    $sql = "INSERT into coins (name, price) VALUES('" . $name . "','" . $price . "')";

    // echo $sql;
    // die();

    if ($connection->query($sql) === TRUE) {
        // echo "Record updated successfully";
    } else {
        // echo "Error updating record: " . $connection->error;
    }

}

$connection->close();
?>

答案 2 :(得分:0)

我已经弄清楚了,感谢大家帮助我一遍又一遍。

由于我的PHP API是用于所有密集用途的“ GET”:

const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

然后调用“ POST”功能将两次关闭API-请参见下面的屏幕截图:

const obj = {
  name: name,
  price: price
};

const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

this
  .http
  .post(uri, obj)

因此,我将其更改为“ GET”功能,并且仅需一次输入即可完美运行

addCoin(name, price) {
    const obj = {
      name: name,
      price: price
    };

    const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

    this
      .http
      .get(uri)
      .pipe(
        catchError(this.handleError), // then handle the error
        map(res => {
          console.log(res);
        })
      )
      .subscribe(res =>
        console.log('Done'));
  }

only one entry

在此过程中,我从(another stackoverflow post - see @nodirabegimxonoyim 's answer)发现,地图在Angular 6中的导入方式不同,并且在.get方法中的使用方式不同

而不是使用

导入
    import 'rxjs/add/operator/map';

您使用以下内容:

import { map } from 'rxjs/operators';

代替.get()。map()

  this
  .http
  .get(uri)
  .map(res => {console.log(res)});

您在.pipe内使用地图

this
  .http
  .get(uri)
  .pipe(
    catchError(this.handleError), // then handle the error
    map(res => {
      console.log(res);
    })
  )

总的来说,以下是我新使用的coin.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { map } from 'rxjs/operators';

@Injectable()
export class CoinService {

  constructor(private http: HttpClient) {}

  addCoin(name, price) {
    const obj = {
      name: name,
      price: price
    };

    const uri = 'http://localhost/ng5crud/api/post-coins/' + name + '/' + price;

    this
      .http
      .get(uri)
      .pipe(
        catchError(this.handleError), // then handle the error
        map(res => {
          console.log(res);
        })
      )
      .subscribe(res =>
        console.log('Done'));
  }

  getCoins() {
    const uri = 'http://localhost/ng5crud/api/get-coins/';
    return this
              .http
              .get(uri)
              .pipe(
                catchError(this.handleError), // then handle the error
                map(res => {
                  console.log(res);
                })
              );
  }

  editCoin(id) {
    const uri = 'http://localhost/ng5crud/api/get-coins-id/' + id;
    return this
              .http
              .get(uri)
              .pipe(
                catchError(this.handleError), // then handle the error
                map(res => {
                  console.log(res);
                })
              );
  }

  updateCoin(name, price, id) {
    const uri = 'http://localhost/ng5crud/api/put-coins/' + id;

    const obj = {
      name: name,
      price: price
    };
    this
      .http
      .post(uri, id)
      .pipe(
        catchError(this.handleError), // then handle the error
        map(res => {
          console.log(res);
        })
      )
      .subscribe(res => console.log('Done'));
  }

  deleteCoin(id) {
    const uri = 'http://localhost/ng5crud/api/delete-coins/' + id;

    return this
              .http
              .get(uri)
              .pipe(
                catchError(this.handleError), // then handle the error
                map(res => {
                  console.log(res);
                })
              );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something happened; please try again later.');
  }
}

非常感谢您的帮助。这使我回到了90年代初,那时程序员很好,我们会互相帮助。