Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
AngularJS TIPS

AngularJS TIPS

サーバーサイドとHTTP POSTで非同期通信するには?($http)

2015年12月14日

AngularJSでHTTP POSTで非同期通信する方法を説明。また、送信データをJSONではなくjQuery形式にする方法や、PHPでJSONデータをデコードする方法も紹介する。

  • このエントリーをはてなブックマークに追加

 別稿「TIPS:サーバーサイドと非同期通信するには?」では、$httpサービスを利用してHTTP GETによる非同期通信を実装してみました。本稿では、引き続き$httpサービスを利用したHTTP POSTの方法を見てみます。前半ではHTTP POSTを利用した場合の問題点を確認した上で、後半では、その対処方法を解説します。

HTTP POSTを利用する場合の注意点

 まずは、別稿のサンプルをHTTP POST対応に書き換えてみましょう(変更したのは太字部分)。テキストボックスに入力された名前に基づいて、「Hello, ●○!」のようなあいさつメッセージを返すことを想定しています。

HTML
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8" />
<title>AngularJS TIPS</title>
</head>
<body ng-controller="MyController">
<form name="myForm" novalidate>
  <label for="name">名前:</label>
  <input id="name" name="name" type="text" ng-model="name" />
  <button ng-click="onclick()">送信</button>
</form>
<div>{{result}}</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script>
angular.module('myApp', [])
  .controller('MyController', ['$scope', '$http', function($scope, $http) {
    $scope.onclick = function() {
      // 1サーバーに対してHTTP POSTでリクエストを送信
      $http({
        method: 'POST',
        url: 'post.php',
        data: { name: $scope.name }
      })
      // 成功時の処理(ページにあいさつメッセージを反映)
      .success(function(data, status, headers, config){
        $scope.result = data;
      })
      // 失敗時の処理(ページにエラーメッセージを反映)
      .error(function(data, status, headers, config){
        $scope.result = '通信失敗!';
      });
    };
  }]);
</script>
</body>
</html>
リスト1 HTTP POSTでサーバーサイドに問い合わせするコード(post.html)
PHP
<?php
// 2ポストデータnameを取得
$data = $_POST['name'];

if (empty($data)) {
  header('HTTP/1.1 500 Internal Server Error');
} else {
  print('Hello,'.$data.'!');
}
リスト2 クライアントからの要求を受けてメッセージを応答するコード(post.php)

 $httpサービスでHTTP POSTリクエストを送信するには、methodパラメーターにPOSTを指定した上で、入力値はdataパラメーターに設定します(1)。HTTP GETではparamsパラメーターを利用していましたが、こちらはクエリ情報を表すものなので、注意してください。また、サーバーサイドではポストデータを受け取るために、スーパーグローバル変数$_GET$_POSTに置き換えています(2)。

 これでうまくいきそうな気がしますが、結果はエラーとなります。

リスト1、2の実行結果(エラー)

 この原因は、開発者ツールでリクエストデータを確認すると明らかです。リクエストデータがJSON(JavaScript Object Notation)で送信されています。$_POSTは、標準で「application/x-www-form-urlencoded」形式のデータを受け取りますので、このままでは解釈できません。結果、$_POST['name']は空となり、エラーが発生しています。

リクエストデータの形式を変換する

 この問題を回避する一つ目の方法は、$httpサービスのデータ形式を変換することです。これには、リスト1を以下のように変更してみましょう。

JavaScript
angular.module('myApp', [])
  .controller('MyController',
      ['$scope', '$http', '$httpParamSerializerJQLike',
      function($scope, $http, $httpParamSerializerJQLike) {
    $scope.onclick = function() {
      $http({
        method: 'POST',
        headers: {
          // 1リクエストヘッダーを設定
          'Content-Type' : 'application/x-www-form-urlencoded;charset=utf-8'
        },
        // 2リクエストデータをjQueryと同様の形式で送信
        transformRequest: $httpParamSerializerJQLike,
        url: 'post.php',
        data: { name: $scope.name }
      })
      .success(function(data, status, headers, config){
        $scope.result = data;
      })
      .error(function(data, status, headers, config){
        $scope.result = '!!通信に失敗しました!!';
      });
    };
  }]);
リスト3 HTTP POSTによるポストデータの形式を変換するコード(post2.html)

 headersパラメーターには、リクエストヘッダーを設定します(1)。ここではContent-Typeヘッダー(データ形式)を、一般的なフォームからの送信フォーマットである「application/x-www-form-urlencoded」で宣言しています。

 ただし、これだけではデータ形式を宣言しているだけなので、データを実際に変換する必要があります。もっとも、これにはAngularJS 1.4*1から導入された$httpParamSerializerJQLikeを呼び出すだけです。$httpParamSerializerJQLikeは、リクエストデータをjQueryと同様の形式でシリアライズするためのサービスです。これをtransformRequest(リクエスト変換関数)パラメーターに渡すことで(2)、リクエストがapplication/x-www-form-urlencoded形式に変換されます。

  • *1 AngularJS 1.3以前では、独自の変換コードをtransformRequestパラメーターに引き渡す必要があります。詳しくは、後日別稿で解説の予定です。

補足:サーバーサイドでJSONデータをデコードする

 クライアントサイドのコードには手を入れず、サーバーサイドでJSON形式のデータを受け取るようにすることもできます。PHPの例ではありますが、他の言語を利用している場合にも、一つの参考としてください。

PHP
<?php
$data = json_decode(file_get_contents('php://input'), TRUE);

if (empty($data['name'])) {
  header('HTTP/1.1 500 Internal Server Error');
} else {
  print('Hello,'.$data['name'].'!');
}
リスト4 JSON形式のデータを処理する方法(post_json.php)

このコードを実行するには、HTMLソースはリスト1に戻して、JSON形式で送信するようにしておく必要がある。

 「php://input」(標準入力)は、リクエスト本体を生データ(ここではJSON文字列)として受け取ります。ここでは、file_get_contents関数でJSON文字列を読み込み、その結果をjson_decode関数でデコード、連想配列に変換(=第2引数でTRUEを指定)しています。あとは、$data['name'] のような形式で、個々の入力項目にアクセスできます。

処理対象:非同期通信 カテゴリ:サービス
API:$http カテゴリ:ng(コアモジュール) > service(サービス)

※以下では、本稿の前後を合わせて5回分(第34回~第38回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

AngularJS TIPS
34. ログをコンソールに出力するには?($log)

AngularJSで開発者ツールのコンソールにログを出力する方法を説明。また、ログの種類や、デバッグログの表示/非表示の切り替え方法も紹介する。

AngularJS TIPS
35. サーバーサイドと非同期通信するには?($http)

AngularJSでサーバーサイドのWeb APIと非同期通信する方法を説明。また、ログの種類や、ショートカットメソッドについても紹介する。

AngularJS TIPS
36. 【現在、表示中】≫ サーバーサイドとHTTP POSTで非同期通信するには?($http)

AngularJSでHTTP POSTで非同期通信する方法を説明。また、送信データをJSONではなくjQuery形式にする方法や、PHPでJSONデータをデコードする方法も紹介する。

AngularJS TIPS
37. JSON形式のWeb APIにアクセスするには?($http)

Web APIと通信する際に問題となるクロスドメイン制約を回避するために使われるテクニック「JSONP」を、AngularJSで実現するための基本的な方法を説明する。

AngularJS TIPS
38. 指定された時間の経過で処理を実行するには?($interval/$timeout)

ミリ秒単位で処理を実行できる、いわゆる「タイマー」である$intervalサービスの基本的な使い方を解説。また、一定時間後に処理を実行する$timeoutサービスについても説明する。

サイトからのお知らせ

Twitterでつぶやこう!