【问题标题】:angular 1.5 $resource, how to set default action, (e.g. transformResponse) for all requests?angular 1.5 $resource,如何为所有请求设置默认操作(例如 transformResponse)?
【发布时间】:2017-03-17 17:31:06
【问题描述】:

我有这样的资源:

angular.module('mymodule')
    .factory('someResource', someResource);

function someResource($resource) {
  return $resource('/something', {}, {
    find  : {method: 'GET', isArray: true, transformResponse: convertFn},
    create: {method: 'POST', isArray: true, transformResponse: convertFn},
    update: {method: 'PUT', isArray: true, transformResponse: convertFn},
  });

  function convertFn(){
    //...
  }

}

是否可以不在每种类型的请求中复制粘贴 transformResponse?定义一些默认的 transformResponse?

可能的解决方案只是通过以编程方式添加属性来修改定义对象,但这样的解决方案看起来很难维护。

angular.module('mymodule')
    .factory('someResource', someResource);

function someResource($resource) {
  var types = {
    find  : {method: 'GET', isArray: true},
    create: {method: 'POST', isArray: true},
    update: {method: 'PUT', isArray: true},
  }

  //add transform to each, es6
  Object.keys(types).forEach(k => types[k].transformResponse = convertFn)

  return $resource('/something', {}, types);

  function convertFn(){
    //...
  }

}

编辑
感谢georgeawg 的想法
另一种方法可能是:为默认值编写一个包装函数,例如:

angular.module('mymodule')
    .factory('someResource', someResource);

function someResource($resource) {
  var types = {
    find  : defaults({method: 'GET'}),
    create: defaults({method: 'POST', isArray: false}),
    update: defaults({method: 'PUT'}),
  }

  return $resource('/something', {}, types);

  function convertFn(){
    //...
  }

  function defaults(opts) {
    return Object.assign({
      isArray: false,
      transformResponse: convertFn
    }, opts)
  }

}

有没有更清洁的解决方案?

【问题讨论】:

    标签: angularjs default angular-resource


    【解决方案1】:

    怎么样:

    angular.module('mymodule')
        .factory('someResource', someResource);
    
    function someResource($resource) {
      return $resource('/something', {}, {
        find  : action('GET'),
        create: action('POST'),
        update: action('PUT')
      });
    
      function action(method) {
          return { method: method,
                   isArray: true,
                   transformResponse: convertFn
                  };
      }
    
      function convertFn(){
        //...
      }
    
    }
    

    使用响应拦截器

    由于$resource 在后台使用$http 服务,response interceptor 可以转换响应:

    app.config(function($httpProvider) {
        $httpProvider.interceptors.push(function() {
          return {
           'request': function(config) {
               //
            },
    
            'response': function(response) {
              if (response.config.url.startsWith("/something") {
                  response.data = convertFn(response.data);
              };
              return response;
    
              function convertFn(data) {
                 //return new data
              }
            }
        });
    });
    

    【讨论】:

    • thx,看起来更简单,但是有一个问题,如果你想修改一个操作选项,比如isArray: false 或者添加新选项。该方法应该像选项的包装器一样,设置默认值。
    • 添加了响应拦截器部分
    【解决方案2】:

    你看过 angular $http interceptors 吗? ngResource 将尊重响应拦截器功能。 Here's a post 详细说明用法。

    【讨论】:

    • 谢谢。拦截器是全局的,然后适用于每个请求/资源。这意味着我应该在拦截器中写像if url .. 这样的条件。看起来更难维护:/
    【解决方案3】:

    可以有一个非常通用的基础 $resource 并继承它。

    我在一个我再也找不到的 SO 帖子上找到了答案。如果有人发现它,请编辑我的帖子以添加它。

    这是我使用的代码:

    angular.baseResourceServiceMaker = function(service){
        return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint', 'Utils',
                function($injector, $resource,TypeService, $http, _, BackEndpoint, Utils){
            this.restUrl = BackEndpoint+'/rest/';
            this.baseName = '';
            this.resource = null;
            // from angular-resource
            var toString= function() {
                var value = [];
                _.forEach(this, function(e) {
                    value.push('' + e);
                    });
                return '[' + value.join(', ') + ']';
              };
              var isObject = function isObject(value) {
                  // http://jsperf.com/isobject4
                  return value !== null && typeof value === 'object';
                };
            var isFile = function(obj) {
              return toString.call(obj) === '[object File]';
            }
    
    
            var isFormData = function(obj) {
              return toString.call(obj) === '[object FormData]';
            }
    
    
            var isBlob = function(obj) {
              return toString.call(obj) === '[object Blob]';
            }
            this.defaultToJson = function(d) {
                  return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? angular.toJson(d) : d;
            };
            this.typeServiceProcessData = function(d){
                return TypeService.processData(d);
            };
            this.typeServiceProcessJsData = function(d){
                return TypeService.processJsData(d);
            };
            this.generateTransformRequestFn = function(mapKeyValues){
                return function(data){
                    var object = {};
                    _.forEach(_.keys(mapKeyValues), function(key){
                        Utils.setAttributeValue(object, key, Utils.getAttributeValue(data, mapKeyValues[key]));
    
                    });
                    return object;
                }
            };
    
            this.addedMethods = {};
            // use of resource will be internal, to handle transformation of data
            // and so on...
            this.getResource = function(){
                if(this.resource == null){
                    var baseResourceUrl = this.restUrl + this.baseName + '/';
                    var baseResourceMethods = {
                            'get':      {method:'GET', transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
                                url:baseResourceUrl+':id'},
                            'create':   {method:'POST', url:baseResourceUrl, transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
                            'update' :  {method:'PUT', transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
                            'search':   {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData], 
                                url:baseResourceUrl+'search/(:search)/:offset/:limit/:order',
                                params: {offset:0, limit:50, order:"creationDate=asc"}
                            },
                            'custom_search':    {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData], 
                                url:baseResourceUrl+':prefix/search/(:search)/:offset/:limit/:order',
                                params: {search:'pk=gt=0',offset:0, limit:50, order:"creationDate=asc"}
                            },
                            'list':     {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData], 
                                url:baseResourceUrl+'search/(pk=gt=0)/0/50/creationDate=asc'
                            },
    
                            'delete':   {method:'DELETE'} 
                        };
                    _.forEach(_.keys(this.addedMethods), function(key){
                        baseResourceMethods[key] = this.addedMethods[key];
                    }, this)
                    this.resource = $resource(baseResourceUrl+':id',
                        {id:'@pk'}, baseResourceMethods
                        );
                }
                return this.resource;
            };
            this.get = function(id){
                this.getResource().get({id:id});
            };
            this.create = function(data){
                this.getResource().create(data);
            };
            this.update = function(data){
                this.getResource().update(data);
            };
            this.search = function(searchQuery){
                this.getResource().search({search:searchQuery});
            };
            this.searchPaginate = function(searchQuery, offset, limit){
                this.getResource().search({search:searchQuery, offset:offset, limit:limit});
            };
            this['delete'] = function(id){
                this.getResource()['delete']({id:id});
            };
               // Finishes the other injections
            $injector.invoke(service, this);
    
        }];
    };
    

    关于这段代码的一些cmets:

    • 函数 isFile/isObject,... 是来自 angular.js 的 c/c,因为我保留了来自 angularJS 的 defaultTransformResponse,这个函数使用了不在我范围内的内部函数,所以我不得不抄送它。
    • 我定义了默认方法 create/update/...
    • 我有一个 typeService,我在其中声明了我的所有类型和字段,因此我可以自动转换类型:例如,我的服务器日期始终是时间戳,所以我在 transformResponse 中将其自动转换为 Javascript Date,而在 transformRequest 中则相反。李>
    • addedMethods 用于添加其他方法。
    • restUrl是所有rest服务的入口点,baseName必须由实现设置,它是资源的入口点。 BackEndPoint 是定义我的应用程序的 contextPath 的常量。

    使用示例:

    .service('ArticleService',angular.baseResourceServiceMaker(['$http', function($http){
          this.baseName = 'article';
          var baseResourceUrl = this.restUrl + this.baseName + '/';
          this.addedMethods.root ={
                  method:'GET', isArray:true, 
                  transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData], 
                  url:baseResourceUrl+'root'
          };
    }]))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-12
      • 2016-03-13
      • 2014-02-20
      • 2016-06-20
      • 1970-01-01
      • 2018-01-28
      • 2012-12-16
      • 2013-10-07
      相关资源
      最近更新 更多