使用 AngularJS 1.4.7 服务调用 ASP.NET Web API 不起作用

Calling ASP.NET Web API using AngularJS 1.4.7 service doesn't work

我在这个项目中使用 Visual Studio 2015 社区版和 AngularJS 1.4.7。

ProductAPIController.cs

public class ProductAPIController : ApiController
{
    [HttpGet]
    public IQueryable<Product> MyProducts()
    {
        List<Product> p = new List<Product>() { new Product {ProductName ="abc", Id = 1, Description = "Desc 1" },
                                                new Product {ProductName ="def", Id = 2, Description = "Desc 2" },
                                                new Product {ProductName ="ghi", Id = 3, Description = "Desc 3" } };
        return p.AsQueryable();
    }
}

Product.cs

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public string Description { get; set; }
}

app.module.js 我按照最佳实践 here

的建议为应用程序模块使用了一个单独的文件
(function () {
    'use strict';
    angular.module('app', ['ngCookies', 'ngRoute'])
})();

homeCtrl.js

这里我在同一个文件中同时使用了服务和控制器。我什至尝试在单独的文件中使用它,但仍然没有用。

(function () {
   'use strict';
   angular.module('app')
   .controller('homeCtrl', ['$scope', '$http', 'ProductService', '$location', function homeCtrl($scope,  $http, ProductService, $location) {
    $scope.products = [];

    $scope.getProducts = function () {
        ProductService.getProducts().then(function (response) {
            $scope.products = response.data;
        }, function (err) {
            alert(err);
        });
      };
   }])
   .service('ProductService', ['$http', function ($http) {
       this.getProducts = function () {
          return $http.get('/api/ProductAPI');
       };
    }]);
})();

index.cshtml

这里我用的是data-ng-repeat="item in products"

<div class="row" ng-app="app">
   <div class="col-md-12" ng-controller="homeCtrl">
      <div class="row">
          <div class="col-md-12">
              <form id="test">
                  <div data-ng-repeat="item in products">
                      <div class="col-sm-4 column productbox">
                          <div class="producttitle">{{item.ProductName}}</div> <div class="action"></div>
                          <div class="productprice">
                              <div class="pull-right">
                                  <div >{{item.Id}}</div> 

                                  <a href="" class="btn btn-danger btn-sm" role="button"><span class="glyphicon glyphicon-arrow-right"></span> Get Quote</a>
                              </div>
                              <div class="pricetext">{{item.Description}}</div>
                          </div>
                      </div>
                  </div>
              </form>
          </div>
      </div>
  </div>
</div>
@section scripts
{
    @Scripts.Render("~/Scripts/angular.js")
    @Scripts.Render("~/Scripts/angular-cookies.min.js")
    @Scripts.Render("~/Scripts/angular-route.js")
    @Scripts.Render("~/Ng/app.module.js")
    @Scripts.Render("~/Ng/homeCtrl.js")
}

我尝试了几种不同的方法,但都失败了。我在这里做错了什么?

编辑

我已经将示例项目上传到 GitHub here 。下载后您可能需要重建项目来同步相关包。

编辑:回答

以备日后参考,万一像我这样的AngularJS初学者正在看这道题,答案如下。 基于@Miyuru Ratnayake 给出的,只是改变了

$scope.getProducts = function () {
 ......
}

function getProducts() { 
   ...
}

并将其称为 before the function 声明。不需要单独的函数 activate()。

所以控制器是这样的:

.controller('homeCtrl', ['$scope', '$cookies', '$http', 'ProductService', '$location', function homeCtrl($scope, $cookies, $http, ProductService, $location) {
    $scope.products = [];
    getProducts();
    function getProducts() { 
        ProductService.getProducts().then(function (response) {
            $scope.products = response.data;
        }, function (err) {
            alert(err);
        });
    };
}])

您应该在控件中的某处调用 getProducts() 方法...我没有看到它被调用了。

通常我会创建一个激活方法,如下所示,并在控件中调用它:

(function () {
    'use strict';
    angular.module('app')
    .controller('homeCtrl', ['$scope', '$cookies', '$http', 'ProductService', '$location', function homeCtrl($scope, $cookies, $http, ProductService, $location) {
        $scope.products = [];

        activate();

        function activate() {
            getProducts();
        }

        function getProducts () {
            ProductService.getProducts().then(function (response) {
                $scope.products = response.data;
            }, function (err) {
                alert(err);
            });
        };
    }])
    .service('ProductService', ['$http', function ($http) {
        this.getProducts = function () {
            return $http.get('/api/ProductAPI');
        };
    }]);
})();

您忘记在 API 调用中定义方法名称,试试这个:

.service('ProductService', ['$http', function ($http) {
   this.getProducts = function () {
      return $http.get('/api/ProductAPI/myProducts');
   };
}]);

默认情况下 ASP.NET Web API 查找“api/{controller}/{id}”,这意味着 ProductAPIController 应该有 GET、POST、PUT、DELETE 等基于 REST 概念的方法来处理模型。

但是在您的 ProductAPIController 代码中,GetProducts() 是用 HTTPGET 装饰的自定义操作方法,但即使这样,Web API 路由也不会理解这一点,直到您定义路由应该是“api/{controller}/{action}/{id}".

打开WebApiConfig.cs,如下更改路由模板并进行测试。 我添加了 {action},以便 Web API 路由在 API 控制器中查找自定义操作。

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

只是一个建议 如果您计划使用 'GetProducts'、'GetProductsByName' 等自定义操作方法;使用此路线模板。

在客户端代码中使用它们之前,始终使用 POSTMAN 或 Fiddler 测试 Web API。我附上了通过 PostMan 测试的 GetProducts 的屏幕截图。

如果您使用 github 中的当前代码版本实际查看浏览器控制台,您会立即发现问题在于函数在您的代码中声明的顺序controller 会导致您尝试执行尚未定义的函数。

我重新订购了您的控制器以执行以下操作:

$scope.products = [];

function activate() {
    $scope.getProducts();
}

$scope.getProducts = function () {
    ProductService.getProducts().then(function (response) {
        $scope.products = response.data;
    }, function (err) {
        alert(err);
    });
};

activate();

此外,在您的 index.cshtml 中,您引用的产品详细信息使用了错误的大小写。

例如,你有这个:

{{item.ProductName}}

什么时候应该是这样:

{{item.productName}}