为啥 AngularJS 应用程序不能在不同的服务器上使用 REST 服务?
Posted
技术标签:
【中文标题】为啥 AngularJS 应用程序不能在不同的服务器上使用 REST 服务?【英文标题】:Why can't AngularJS app consume REST service on different server?为什么 AngularJS 应用程序不能在不同的服务器上使用 REST 服务? 【发布时间】:2016-03-12 18:25:36 【问题描述】:如何从运行在不同服务器上的 AngularJS 应用调用 Spring Boot REST api? 具体来说,如何修改以下代码来完成此操作?
我正在使用 the tutorial at this link 学习 Spring Boot。该应用程序的第一部分和第二部分运行完美。在part three of the tutorial (which is at this link) 中,我们将REST api 移动到不同的资源服务器,原型为localhost:9000
。
本教程使用 groovy 制作资源服务器应用程序,但我正在尝试将其修改为 Java。
当我定义/home/username/workspacename/resource/src/main/java/com/example/ResourceApplication.java
时,我可以访问localhost:9000
的Web 服务,如下所示。
但是,在 localhost:8080
上运行的 AngularJS 应用程序无法从 localhost:9000
访问 Web 服务。当我然后添加CorsFilter.java
如下所示并使用kill $(lsof -t -i:9000)
和mvn spring-boot:run
重新启动服务时,我无法再查看localhost:9000
的数据,并且我仍然无法从应用程序访问Web 服务localhost:8080
。
我没有对代码进行任何其他更改,我停在教程中创建CorsFilter.java
并重新启动应用程序的位置。 那么如何更改下面的代码以从localhost:9000
获取Web 服务,以便从localhost:8080
的gui 应用程序访问?
资源服务器中两个更改类的代码如下:
/home/username/workspacename/resource/src/main/java/com/example/ResourceApplication.java
是:
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@SpringBootApplication
@RestController
public class ResourceApplication
@RequestMapping("/")
public Map<String,Object> home()
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
public static void main(String[] args)
SpringApplication.run(ResourceApplication.class, args);
/home/username/workspacename/resource/src/main/java/com/example/CorsFilter.java
是:
package com.example;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class CorsFilter implements Filter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Max-Age", "3600");
if (request.getMethod().equals("OPTIONS"))
try chain.doFilter(req, res);
catch (IOException e) e.printStackTrace();
catch (ServletException e) e.printStackTrace();
else
public void init(FilterConfig filterConfig)
public void destroy()
hello.js
文件在 GUI 应用程序中localhost:8080
的代码是:
angular.module('hello', [ 'ngRoute' ])
.config(function($routeProvider, $httpProvider)
$routeProvider.when('/',
templateUrl : 'home.html',
controller : 'home'
).when('/login',
templateUrl : 'login.html',
controller : 'navigation'
).otherwise('/');
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
)
.controller('home', function($scope, $http)
$http.get('http://localhost:9000/').success(function(data)
$scope.greeting = data;
)
)
.controller('navigation',
function($rootScope, $scope, $http, $location)
var authenticate = function(credentials, callback)
var headers = credentials ? authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
: ;
$http.get('user', headers : headers).success(function(data)
if (data.name)
$rootScope.authenticated = true;
else
$rootScope.authenticated = false;
callback && callback();
).error(function()
$rootScope.authenticated = false;
callback && callback();
);
authenticate();
$scope.credentials = ;
$scope.login = function()
authenticate($scope.credentials, function()
if ($rootScope.authenticated)
$location.path("/");
$scope.error = false;
else
$location.path("/login");
$scope.error = true;
);
;
$scope.logout = function()
$http.post('logout', ).success(function()
$rootScope.authenticated = false;
$location.path("/");
).error(function(data)
$rootScope.authenticated = false;
);
);
正在进行的研究:
按照@meriton 的要求,我在firefox 中打开了开发者工具,然后请求localhost:8080
。当我放大到网络选项卡以获取 GET 请求的响应标头以调用嵌入在 localhost:8080
请求中的 localhost:9000
时,我得到了以下请求标头:
Access-Control-Allow-Headers: x-requested-with
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Content-Length: 0
Date: Tue, 08 Dec 2015 00:41:01 GMT
Server: Apache-Coyote/1.1
access-control-allow-origin: *
并且Network
选项卡中还显示了以下请求标头:
Host: localhost:9000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8080/
Origin: http://localhost:8080
Connection: keep-alive
【问题讨论】:
“我无法再查看 localhost:9000 的数据”是什么意思?你有错误吗?如果是这样,什么错误? @meriton 我只是得到一个白色的空白页。浏览器没有错误。我将使用开发人员工具查看这些日志中显示的内容。我还应该去哪里看? 开发者工具。具体来说,javascript 控制台,如果服务器响应异常,则网络选项卡。如果 javascript 控制台上没有错误,并且响应看起来有效,则必须调试发送 rest 请求并处理响应的代码。 @meriton 我刚刚在 Firefox 中调用了localhost:9000
,并打开了开发者工具。调试器选项卡显示no sources
和no stack frames
。当我从网络选项卡再次调用它时,日志列出了对 /
的调用,响应为 0kb。
什么是 HTTP 状态和响应的标头?也就是说,这听起来像是您的服务器行为不端。
【参考方案1】:
过滤器是 servlet 的拦截器,它可以选择自己处理请求,或者使用 chain.doFilter(req, res)
将其传递给过滤器链中的下一个过滤器或 servlet。您的过滤器仅传递选项请求,所有其他请求(例如 GET 请求)既不传递也不响应:
if (request.getMethod().equals("OPTIONS"))
try chain.doFilter(req, res);
catch (IOException e) e.printStackTrace();
catch (ServletException e) e.printStackTrace();
else
由于从未写入响应,因此它保持为空,这解释了服务器回复的 0KB 响应。
至于解决这个问题,您应该正确转录示例代码,即教程代码
if (request.getMethod()!='OPTIONS')
chain.doFilter(req, res)
else
应该变成
if (!request.getMethod().equals("OPTIONS"))
chain.doFilter(req, res)
else
(注意否定!)或者更好
if (request.getMethod().equals("OPTIONS"))
// preflight request
else
chain.doFilter(req, res)
但是,使用CORS-Support provided by Spring MVC 可能比自己实现它更容易。
【讨论】:
谢谢。你发现了我的错误。我把!=
变成了string.equals
。应该是!string.equals
。
我在同一个教程中还有另一个问题。您是否也愿意对此发表评论?这是链接:***.com/questions/34235409/…以上是关于为啥 AngularJS 应用程序不能在不同的服务器上使用 REST 服务?的主要内容,如果未能解决你的问题,请参考以下文章
为啥图像网格不能在 angularjs ng-repeat 中扩展但 javascript 工作正常?
为啥我应该在 AngularJS 中选择使用服务而不是工厂? [复制]