1

I am not sure if I have CORS issue with my Angular app calling WebAPI. My ASP.Net MVC/Angular App1 and the Web API are hosted on the same IIS server. For example, the url for App1 is https://example.com/App1. The Web API url is http://example.com/wcf_webapi/clinicaluserprofile/api/{userNetworkName}. On a MVC view page there is a <div ng-include=" 'path to an angular view template' "></div> that uses $resource to get data from the WebAPI. In Visual Studio 2013 IDE when I hit F5 to debug on my development workstation everything works as expected. I can see a call to the Web API in IE Developer Tools' Network tab. However, when I publish App1 to that IIS server, the Web API call doesn't seem to be made. Instead, I saw access denied in the IE Developer Tools Console. I have another MVC/Angular App2 on the same IIS in different folder with almost identical Angular code. The url for App2 is https://example.com/App2. It doesn't get access denied. Can you help? Thanks.

Here is the Config.js

/// <reference path="C:\Users\myName\Source\Repos\Apps\App1\Scripts/angular.js" />
(function() {
  'use strict'

  var moduleName = 'configModule';
  angular.module(moduleName, []);

  var apiKeyValuePair = {
    'api': {
      centralAdmin: 'http://example.com/wcf_webapi/CentralAdmin/api/',
      fsr: 'http://example.com/FSRManagement/api/',
      userProfile: 'http://example.com/wcf_webapi/clinicaluserprofile/api/'
    }
  }

  var userKeyValuePair = {
    'userAccount': {
      //this is bootstraped and updated in MVC view page <script> block
      userName: ''
    }
  }

  //populate the constant collection
  angular.forEach(apiKeyValuePair, function(val, key) {
    angular.module(moduleName).constant(key, val);
  });
   
  //populate the value collection
  angular.forEach(userKeyValuePair, function(val, key) {
    angular.module(moduleName).value(key, val);
  });
})();

Here is the App.js

/// <reference path="C:\Users\myName\Source\Repos\Apps\App1\Scripts/angular.js" />
(function() {
  'use strict'
  var moduleName = 'ufsrAppModule';
  var requiredModules = [
    'configModule',
    'fsrFactoryModule',
    'userFsrFactoryModule',
    'userProfileFactoryModule'
  ]
  var app = angular.module(moduleName, requiredModules);
  //  app.config(
  //    )

})();

Here is the userProfileFactory.js

/// <reference path="C:\Users\myName\Source\Repos\Apps\App1\Scripts/angular.js" />
/// <reference path="C:\Users\myName\Source\Repos\Apps\App1\Scripts/angular-resource.js" />
/// <reference path="../config.js" />

(function() {
  'use strict';

  var moduleName = 'userProfileFactoryModule';
  var factoryName = 'userProfileFactory';
  var requiredModules = ["ngResource", "configModule"];
  var dependencyInjection = ['$resource', 'api', 'userAccount', internalFunc];

  angular.module(moduleName, requiredModules)
    .factory(factoryName, dependencyInjection);

  function internalFunc($resource, api, userAccount) {
    function getUserProfile() {
      return $resource(api.userProfile + 'user/profile/:NN', {
        NN: '@NN'
      });
    };

    return {
      //usage: userProfileFactory.getUserProfile()
      getUserProfile /*public method*/ : getUserProfile /*internal method above*/
    }
  }
})();

Here is the Angular template being included in the MVC view

<div ng-controller="ufsrController as ufsrCtrl">
  <div class="container">
    <div class="row">
      <div class="widget-body">
        <div class="col-md-6 col-sm-6 fa fa-border">
          <div class="row">
            <div class="col-md-2 col-sm-2"><label class=" form-label">Facility:</label></div>
            <div class="col-md-9 col-sm-9" style="padding: 0; margin: 0">
              <select ng-model="ufsrCtrl.facility" name="facility"
                      ng-options="fac.FacilityID as fac.FacilityName for fac in ufsrCtrl.facilities"
                      ng-disabled="(ufsrCtrl.facilities === undefined || ufsrCtrl.facilities.length <= 0)"
                      ng-change="ufsrCtrl.facilityChanged()"
                      class="form-control"></select>
            </div>
            <!--
              <div class="col-md-1 col-sm-1">
                <i ng-show="ufsrCtrl.facilities === undefined || ufsrCtrl.facilities.length <= 0" class="fa fa-refresh fa-spin"></i>
              </div>
            -->
          </div>

          <div class="row">
            <input type="text" name="service" ng-model="ufsrCtrl.service.ServiceName" style="display:none" />
            <div class="col-md-2 col-sm-2"><label class="form-label">Service:</label></div>
            <div class="col-md-9 col-sm-9" style="padding: 0; margin: 0">
              <select ng-model="ufsrCtrl.service" name="serviceId"
                      ng-options="ser.ServiceName for ser in ufsrCtrl.services track by ser.ServiceID "
                      ng-disabled="(ufsrCtrl.services === undefined || ufsrCtrl.services.length <= 0)"
                      ng-change="ufsrCtrl.serviceChanged()"
                      class="form-control">
                <option value="">-- Choose Service --</option>
              </select>
            </div>
            <div class="col-md-1 col-sm-1">
              <i ng-show="ufsrCtrl.services === undefined || ufsrCtrl.services.length <= 0" class="fa fa-refresh fa-spin"></i>
            </div>

          </div>

          <div class="row">
            <input type="text" name="role" ng-model="ufsrCtrl.role.RoleName" style="display:none" />
            <div class="col-md-2 col-sm-2"><label class="form-label">Role:</label></div>
            <div class="col-md-9 col-sm-9" style="padding: 0; margin: 0">
              <select ng-model="ufsrCtrl.role" name="roleId"
                      ng-options="role.RoleName for role in ufsrCtrl.roles track by role.FacilityServiceRoleID"
                      ng-disabled="(ufsrCtrl.roles === undefined || ufsrCtrl.roles.length <= 0)"
                      ng-change="ufsrCtrl.roleChanged()"
                      class="form-control">
                <option value="">-- Choose Role --</option>
              </select>
            </div>
            <div class="col-md-1 col-sm-1" title="select a Service to dismiss me">
              <i ng-show="ufsrCtrl.roles === undefined || ufsrCtrl.roles.length <= 0" class="fa fa-refresh fa-spin"></i>
            </div>


          </div>

          <!--<div class="form-group">
            <button class="btn btn-primary" ng-disabled="(ufsrCtrl.role === undefined)" ng-click="ufsrCtrl.add()" disabled="disabled">
              Add new role {{ufsrCtrl.role.FacilityServiceRoleID}} ({{ufsrCtrl.role.RoleName}})
            </button>
            <span ng-show="(ufsrCtrl.postStatus === true)" class="alert-success"><i class="fa fa-check"></i>Success</span>
            <span ng-show="(ufsrCtrl.postStatus === false)" class="alert-danger"><i class="fa fa-warning"></i>Failure</span>
          </div>-->
        </div>
        <div class="col-md-6 col-sm-6">
          <ul style="list-style-type:disc">
            <li>Select your service then role from the dropdown lists.  The rotating icons will disappear when the selected data is retrieved from the database.</li>
            <li>Check the accuracy of your name and email address and correct them address if incorrect</li>
            <li>Click <b>Send</b> button to send email to the administrators</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</div>

Here is the controller.js where "Access denied" occurred on line

userProfileFactory.getUserProfile().get({ NN: userAccount.networkName }

/// <reference path="C:\Users\myName\Source\Repos\Apps\App1\Scripts/angular.js" />
/// <reference path="UserProfile/userProfileFactory.js" />
(
  function() {
    'use strict';

    var moduleName = 'ufsrAppModule';
    var controllerName = 'ufsrController';
    var dependencyInjection = ['api', 'userAccount', 'userProfileFactory', 'fsrFactory', 'userFsrFactory', internalFunc];

    angular.module(moduleName)
      .controller(controllerName, dependencyInjection);

    function internalFunc(api, userAccount, userProfileFactory, fsrFactory, userFsrFactory) {

      var vm = this; //controller AS in ng-controller, do not use $scope

      vm.postStatus = undefined;
      vm.deleteStatus = undefined;
      vm.showFSRSpinner = true;

      //the following REST call to the WebAPI is access denied
      userProfileFactory.getUserProfile().get({
          NN: userAccount.networkName
        },
        function(data) {
          //debugger;

          vm.userProfile = data;

          if (vm.userProfile != undefined || vm.userProfile != null) {
            if (vm.userProfile.SimpleUser != undefined)
              vm.userId = vm.userProfile.SimpleUser.UserID;

            if (vm.userProfile.UserFSRs != null && vm.userProfile.UserFSRs !== undefined) {
              if (vm.userProfile.UserFSRs.length > 1) {
                vm.role === undefined;
                vm.deletable = true;
              }
            }

            //due to the userProfileFactory uses async to make REST api call
            //the vm.userProfile.InferredFacility won't be avaialbe until the success callback is executed
            //and until then the vm.facilities will make api call to fsrFactory.facility().get()

            vm.facility; //for binding to facility dropdown selected item (ng-model)

            //the fsrFacility.facility().get() returns an object
            //but the vm.facilities is bound to ng-option in the <select> element which expects an array
            //so the returned object needs to be wrapped in aryFacility array

            var aryFacility = [];

            var tempFacilityObj = fsrFactory.facility().get({
              id: vm.userProfile.InferredFacility.FacilityID
            }); //get only facility of the user's inferred facility
            aryFacility.push(tempFacilityObj);
            console.log(aryFacility);

            vm.facilities = aryFacility;
            console.log(vm.facilities);

            vm.facility = vm.userProfile.InferredFacility.FacilityID;
            console.log(vm.facility);

            vm.postStatus = undefined;
            vm.services = undefined;
            vm.roles = undefined;
            vm.services = fsrFactory.service().query({
              FacilityID: vm.facility
            });
          }
        }
      );

      vm.facilityChanged = function() {
        vm.postStatus = undefined;
        vm.services = undefined;
        vm.roles = undefined;
        vm.services = fsrFactory.service().query({
          FacilityID: vm.facility
        });
      }

      vm.service; //for binding to service dropdown selected item (ng-model)
      vm.serviceChanged = function() {
        console.log(vm.service);
        vm.postStatus = undefined;
        vm.roles = undefined;
        vm.roles = fsrFactory.role().query({
          FacilityID: vm.facility,
          ServiceID: vm.service.ServiceID
        });
      }

      vm.role; //for binding to role dropdown selected item (ng-model)
      vm.roleChanged = function() {
        console.log(vm.role);
        vm.postStatus = undefined;
      }

      vm.add = function() {
        //debugger;
        vm.postStatus = undefined;
        vm.deleteStatus = undefined;
        userFsrFactory.addUserFSR().save({
            userId: vm.userId,
            fsrId: vm.role.FacilityServiceRoleID
          },
          function() {
            alert("New role added");
            vm.postStatus = true;

            //vm.userProfile.UserFSRs.push({
            //  UserFacilityServiceRoleID: vm.userId,
            //  dtoFSR: {
            //    FacilityName: vm.facility.FacilityName,
            //    ServiceName: vm.service.ServiceName,
            //    RoleName: vm.role.RoleName,
            //  }
            //})
            userProfileFactory.getUserProfile().get(function(data) {
              vm.userProfile = data; //after add requery userProfile to get the list of user FSR
              if (vm.userProfile.UserFSRs != undefined && vm.userProfile.UserFSRs.length > 1) {
                vm.deletable = true;
                vm.disabled = false;
                vm.role = undefined;
              }
            });
          },
          function() {
            alert("add role failed");
            vm.postStatus = false;
          });
      }

      vm.delete = function(idx, ufsrId) {
        vm.postStatus = undefined;
        vm.deleteStatus = undefined;
        userFsrDeleteFactory.deleteUserFSR().delete({
            userId: vm.userId,
            ufsrId: ufsrId
          },
          function() {
            alert("Delete Succeeded");
            vm.deleteStatus = true;

            vm.userProfile.UserFSRs.splice(idx, 1); //remove from the fsr array
            if (vm.userProfile.UserFSRs != undefined && vm.userProfile.UserFSRs.length > 1)
              vm.deletable = true;

            //$route.reload();
            //location.reload(true); //jquery
          },
          function() {
            alert("Delete failed")
            vm.deleteStatus = false;
          }
        );
      }
    }
  })();

2
  • Do you have cors enabled in your webapi? your localhost operates on different ports if they are different solution, unless they are under same solution you would get cross origin request error Commented Jun 11, 2016 at 9:40
  • Thanks for the help. The WebApi is in a different solution and no CORS enabled. The App1 and App2 are in different solutions. I wonder why App2 works but not App1. The only difference is the Angular code in App2 has everything in one module in one js file, except the ngResource.js. The App1 is more modularized into five modules. Commented Jun 11, 2016 at 16:46

2 Answers 2

1

In your App1 run this command in your package manager console

Install-Package Microsoft.AspNet.WebApi.Cors

Add this config.EnableCors(); in your webapiconfig.cs before this line --> config.Routes.MapHttpRoute

Add this attribute in your controller [EnableCors(origins: "*", headers: "*", methods: "*")] . Doing so will allow you to accept requests from any domain with any Http Verbs.

Also I would suggest you to make a wrapper for all the httpRequest in App2 and inject that in your controller's factory service.

Reference

Sign up to request clarification or add additional context in comments.

Comments

1

Thank you all the help. I just found out that the MVC global.asax.cs has added a filter to require HTTPS, but the WebAPI endpoints in the config.js are using HTTP. Thus the CORS issue occurred. In this example, the url to App1 is https://example.com/App1. The WebAPI is http://example.com/wcf_webapi/clinicaluserprofile/api/{userNetworkName}. I changed the WebAPI endpoints to use HTTPS protocol, then everything worked.

1 Comment

Glad that you found the issue by yourself :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.