【问题标题】:Using a Relative Path for a Service Call in AngularJS在 AngularJS 中使用相对路径进行服务调用
【发布时间】:2013-06-09 16:19:49
【问题描述】:

我有以下代码,在部署到测试服务器之前运行良好:

$scope.getUserList = function (userName) {
    $http({
        method: "get",
        url: "GetUserList",
        params: { userName: userName }
    }).
        success(function (data) {
            $scope.users = data;
        }).
        error(function () {
            alert("Error getting users.");

问题是我部署到了一个虚拟目录,下面的调用试图从服务器根目录访问 GetUserList。这是有道理的,我知道有很多方法可以解决它。

我想知道的是正确以在 Angular 中可移植和可维护的方式引用服务 URL 的方式。

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    我建议在头部使用 HTML 基本标记,并编码与此相关的所有路径。例如,在 ASP.NET 中,您可以获得对应用程序基础的引用,它可能是也可能不是站点的根路径,因此使用基础标记会有所帮助。奖励:它也适用于所有其他资产。

    你可以有这样的基本路径:

    <base href="/application_root/" />
    

    ...然后像“foo/bar.html”这样的链接实际上是/application_root/foo/bar.html。

    我喜欢使用的另一种方法是将命名链接放在标题中。我经常会在一个位置有一个 API 根,而在其他地方有一个指令模板根。在头部,然后我将添加一些这样的标签:

    <link id="linkApiRoot" href="/application_root/api/"/>
    <link id="linkTemplateRoot" href="/application_root/Content/Templates/"/>
    

    ... 然后在模块中使用 $provide 来获取链接 href 并将其公开给服务和指令,如下所示:

    angular.module("app.services", [])
        .config(["$provide", function ($provide) {
            $provide.value("apiRoot", $("#linkApiRoot").attr("href"));
        }]);
    

    ...然后将其注入到这样的服务中:

    angular.module("app.services").factory("myAdminSvc", ["apiRoot", function (apiRoot) {
        var apiAdminRoot = apiRoot + "admin/";
        ...
    

    只是我的意见。为您的应用程序做最简单的事情。

    【讨论】:

    • 也许使用 $provide.constant 而不是 $provide.value 更好?常量在配置阶段也可用(例如在路由期间)。你怎么看?
    • 但是你如何处理测试localhost:####?如果我们将基本 url 存储在 Web.Config 中(这样我们可以在发布时进行转换)而不是 &lt;header&gt; 部分,会不会更好?
    • 您可以使用$provide.value("apiRoot", $("#linkApiRoot").attr("href")); 创建对jQuery 的依赖,而您可以使用纯javascript,加上一些jqLit​​e:var elem = angular.element(document.querySelector('#linkApiRoot')); $provide.value("apiRoot", elem.attr("href"));
    • 感谢您的提示,从 IIS Express 切换到本地 IIS 时遇到了同样的问题。但我没有对基本 url 进行硬编码,而是插入了默认操作。 &lt;base href="@Url.Action("Home", "Index")" /&gt; 它按预期工作!!
    • 更新上一条评论:如果$http.get('/api/app/' + action) 中的路径以正斜杠开头,如本例所示,则忽略基本标记。正确的方法是:$http.get('api/app/' + action).
    【解决方案2】:

    我建议定义一个包含全局配置的模块,然后您可以将其传递给您的应用程序:

    // Module specific configuration
    angular.module('app.config')
      .value('app.config', {
        basePath: '/' // Set your base path here
      });
    

    然后,借助 AngularJS 依赖注入,您可以从应用程序中的任何位置访问它:

    // Make sure your config is included in your module
    angular.module('app', ['app.config']);
    
    // Access your config e.g. in a controller
    angular.module('app')
      .controller('TestCtrl', ['$scope','app.config', function($scope, config){
    
        // Use config base path to assemble url
        $scope.url = config.basePath + 'GetUserList';
        ...
      }]);
    

    每当基本路径更改时(例如,当您更改为另一个服务器或主机时),您只需在全局配置中更改它即可。

    【讨论】:

    • 我喜欢这个。一条路是不够的;您可能有模板路径、API 路径等。
    • 确实,您可以在配置中添加任意数量的属性,并在应用程序的任何位置使用它们。
    • @jvandemo 在解决配置依赖关系时,angular如何知道config引用app.config,我一直想知道angular如何知道ng引用angular,它是否使用参数的顺序?
    • 正是我需要用 Symfony2 粘合我的 AngularJS 应用程序!感谢分享!
    • 这个答案看起来很简单,但在可重用性方面实际上很差。为了让您在另一个应用程序中使用您的模块,您还需要使用这个 app.config 模块。
    【解决方案3】:

    我无法使用&lt;base&gt; 标签,因为我的应用程序是从另一个来源动态地在弹出窗口中创建的。我正在考虑在这个线程中使用其他选项来使用 basePath 变量之类的东西,并在每个 $http, ng-src, templateUrl 等中使用它

    但这有点开销,构建了一个拦截器,可以在生成 xhr 之前更改每个 url

    var app = angular.module("myApp", []);
    
    app.config(["$httpProvider", function($httpProvider) {
        $httpProvider.interceptors.push('middleware');
    }]);
    
    app.factory('middleware', function() {
        return {
            request: function(config) {
                // need more controlling when there is more than 1 domain involved
                config.url = "//example.com/api/" + config.url
                return config;
            }
        };
    });
    
    app.controller("Ctrl", ["$http", function($http) {
        $http.get("books"); // actually requestUrl = http://example.com/api/books
    }])
    

    还有html

    <div ng-include src="'view'">
        <!-- actually src = http://example.com/api/view -->
    </div>
    

    但我确实建议使用&lt;base&gt; 标签,除非您使用的是 window.popup()

    【讨论】:

    • 这是一个很好的解决方案!但是,当您通过 xhr 获取视图时,它将使用这个提供的 url!
    • @ammt 我知道,那是我的意图
    • 只有$http 可以使用这个中间件吗?但不适用于 ng-include/template。因为我想为我的 api 设置一个相对路径。
    • @MaicolBen 您可以为您的 api 添加前缀并验证 URL,以便它以 /api/.../ 或其他内容开头。否则 ngResorce 可能是最好的选择
    【解决方案4】:

    使用 $location 服务 - 它会返回您的路径、哈希值、服务器地址......您需要的一切!您可以拨打 $location.path()+"/GetUserList" 或类似的电话。

    请看这里:http://docs.angularjs.org/guide/dev_guide.services.$location

    【讨论】:

    • $location.path() 似乎总是空白。我只是像这样将它注入我的控制器中,还是我需要做的其他事情? var saController = 函数 ($scope, $http, $location) {}
    • 抱歉,回复晚了,但在我们的应用程序中,我们将 $locationProvider 配置为支持 html5Mode,这似乎启动了它。代码如下所示:.config([ '$locationProvider', function($locationProvider) { $locationProvider.html5Mode({ enabled: true, requireBase: false }); } ])
    【解决方案5】:

    我遇到了类似的问题 我用 MVC 页面中的一行代码解决了它

    <base href="~/" />
    

    【讨论】:

    • 这是迄今为止最简单和最好的修复方法。这解决了我的打字稿指令 templateUrl: 问题。在我使用反引号并将我的 html 添加到模板之前:
    【解决方案6】:

    接受的答案帮助了我。我正在使用 Angular 为我的 MVC 应用程序提供服务。 我采取了额外的步骤,以便我的 baseUrl 可以在我的 Angular 控制器中用于 Web api 调用或访问模板。

    使用 ng-init 设置 baseUrl(来自服务器端填充值)

    <html ng-app="app" ng-controller="AppController">
    <head>
        <base href="{{baseUrl}}" ng-init="baseUrl = '@Model.BaseUrl'" />
    </head>
    

    角度控制器

        $scope.manageCustomerGroups = function () {
            openDialog($scope.baseUrl + 'content/templates/customerGroups.html');
        }
    

    【讨论】:

      【解决方案7】:

      一种简单的方法,我使用的是 ASP.NET Razor (Web MVC),所以我获取了应用程序路径并将其作为应用程序的基础。

      <head>
      
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>Title</title>
      
      <meta name="description" content="">
      @{ var appPath = Request.ApplicationPath.ToString();
          if (!appPath.EndsWith("/")) {
              appPath = appPath + "/";
          }
      }
      <base href="@appPath" />
      

      【讨论】:

        【解决方案8】:

        我只是将所有 URL 设为相对。

        url: "../GetUserList",
        

        改为

        url: "GetUserList",
        

        &lt;base href="/application_root/" /&gt; 的硬编码对我来说听起来不是个好主意,因为您可能需要将此环境更改为环境,并且依赖于虚拟目录名称。

        【讨论】:

          【解决方案9】:

          在 ng-init 函数中传递一个包含虚拟目录值的参数(int ASP.NET 存储在 Request.ApplicationPath 中)。

          <div ng-controller="ControllerName" ng-init="init('@Request.ApplicationPath')">
          

          在 Angular 控制器中,在每个 http 调用中使用这个值作为 URL 的前缀。您可以使用此功能来组合路径

          function combinePath(path1, path2) {
              if (path1 == null) {
                  return path2;
              }
              var last = path1.slice(-1);
              var first = path2.charAt(0);
          
              if (last == '/' && first == '/') {
                  path1 = path1.substring(0, path1.length - 1);
              }
              return path1 + path2;
          }
          

          【讨论】:

          • 这个答案中缺少一些东西。 init() 函数有什么作用?它是如何连接到combinePath() 函数的?最后,它是如何在服务中使用的?
          猜你喜欢
          • 2020-03-15
          • 1970-01-01
          • 2015-05-20
          • 2015-08-18
          • 1970-01-01
          • 1970-01-01
          • 2019-03-30
          • 1970-01-01
          • 2017-08-17
          相关资源
          最近更新 更多