Angular跨域请求被阻止

25
我正在试图将我的Angular应用程序与Express上的简单REST服务器连接起来。该服务器仅在响应请求时发送json数据。为了添加CORS支持,我使用了npm上的cors模块。在Angular应用程序中,我按照此问题的说明添加了HttpHeaders: Angular CORS request blocked

这是我在express中设置cors选项的代码:

`
// async CORS setup delegation
function corsOptsDelegator(req, cb) {
    let opts = {},
        origin = req.header('Origin');
    if(imports.allowedOrigins.indexOf(origin) === 1) opts.origin = true;
    else opts.origin = false;
    opts.optionsSuccessStatus = 200;
    opts.methods = ['POST', 'GET']; // allowed methods
    opts.credentials = true; // for passing/setting cookie 
    opts.allowedHeaders = ['Content-Type', 'Accept', 'Access-Control-Allow-Origin']; // restrict headers 
    opts.exposedHeaders = ['Accept', 'Content-Type']; // for exposing custom headers to clients; use for hash
    cb(null, opts);
}
`

这是我如何将它添加到全局的get处理程序中:

`
app.get('/', cors(corsOptsDelegator), (res, req, nxt) => {
    // only for adding cors on all requests
    nxt();
});
`

以下是我设置 Angular 服务的步骤:

`

export class ContentGetterService {

  private root: string;
  private corsHeaders: HttpHeaders;
  //private contents: string;

  constructor(private http: HttpClient) {
    this.root = 'http://localhost:8888';
    this.corsHeaders = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Access-Control-Allow-Origin': 'http://localhost:4200'
    });
    //this.contents = '';
   }

  getContent(subs: Array<string>): Observable<IContent> {
    return (() => {
      return this.http.get<IContent>( (() => {
        let r = this.root;
        subs.forEach((s, i, a) => {
          if(i === a.length-1) {
            r += s;
          }
          else {
            if(s !== '/') {
              r += s;
            }
          }
        });
        return r;
      })(), {
        headers: this.corsHeaders
      });

    })();
  }
  }

浏览器警告:跨源请求被阻止:同源策略禁止读取远程资源http://localhost:8888/。(原因:缺少CORS头‘Access-Control-Allow-Origin’)。

谢谢。

7个回答

17

如果您正在使用angular-cli,您可以使用代理:

proxy.conf.json

{
  "/api": {
    "target": "http://localhost:8888",
    "secure": false
  }
}

将所有以/api为前缀的请求重定向到localhost:8888,无需任何跨域问题。

官方文档:https://angular.io/guide/build#proxying-to-a-backend-server


2
这是一个可行的解决方案,但我坚持使用cors。感谢你的努力。 - Abrar Hossain
12
即使按照你上述提到的代理方式,我仍然遇到了这个问题。 - Asma Rahim Ali Jafri
对我来说有效。但是对于Angular版本>6,有更新的文档。https://angular.io/guide/build#proxy-multiple-entries - Liebster Kamerad
3
请注意,此方法仅适用于开发环境,而不适用于生产环境。 - Kavinda Jayakody
我无法在我的开发环境中使其工作。我应该在哪里提到这是一个开发环境? - Prasannjeet Singh

8
首先,客户端的问题是由Firefox选择处理预检CORS而导致的。它使用了OPTIONS方法而不是GET方法。从我的代码中可以看出,我没有任何处理OPTIONS的处理程序。经过一些搜索,我找到了这篇github上的文章:Express CORS middleware。使用这个中间件并对我的客户端请求头进行以下修改,我成功地让它运行起来了。以下是代码:
CORS中间件:
function myCors(req, res, nxt) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:4200');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Access-Control-Allow-Origin, Content-Type, Accept, Accept-Language, Origin, User-Agent');
    if(req.method === 'OPTIONS') {
        res.sendStatus(204);
    }
    else {
        nxt();
    }
}`

将其挂载到应用实例上:

app.use(myCors);

在 Angular 客户端服务中:

this.corsHeaders = new HttpHeaders({
  'Content-Type': 'application/json',
});
// ... adding it to the request
getContent(subs: Array<string>): Observable<IContent> {
return (() => {
  return this.http.get<IContent>( (() => {
    let r = this.root;
    subs.forEach((s, i, a) => {
      if(i === a.length-1) {
        r += s;
      }
      else {
        if(s !== '/') {
          r += s;
        }
      }
    });
    return r;
  })(), {
    headers: this.corsHeaders
  });
 
})();
}

感谢大家的时间和努力。 编辑 感谢 @neutrino 指出,在请求中添加Access-Control-*头是没有意义的(也没有效果)。

2
使用 Access-Control-Allow-Origin 作为请求头不正确吗?我认为这个头应该只用于服务器响应中。我认为您可以完全删除请求头代码,只要服务器返回正确的头信息,它仍然可以正常工作。 - Neutrino
@neutrino 感谢您指出这一点。现在我更好地理解了,知道它只是错误的! - Abrar Hossain

4

在开发环境(本地主机)中,您可以轻松完成此操作,而无需遵循任何繁琐的步骤。如果您使用的是Firefox浏览器,则可以简单地下载https://addons.mozilla.org/firefox/addon/cors-everywhere/,我认为Chrome浏览器也必定有类似的扩展程序。下载后,它将出现在Firefox菜单栏中,然后您可以单击它以激活它,这样您就可以自动发送包含所有请求头的请求,并访问所有API。对于生产环境,您需要通过将access-control-allow-origin添加到“*”(全部)来从服务器进行配置。


是的,Chrome 也有类似的东西:https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf - wbartussek

2
如果您正在使用Angular CLI、NPM、Spring Data JPA和Tomcat 9进行工作,我终于找到了一种解决方案,可以在应用服务器(Tomcat)上运行的前端Web应用程序(Angular)和后端Web应用程序(Spring Data JPA)之间进行通信。
在开发环境中:
如果您在默认端口4200上运行ng serve,则Spring DAO存储库中的@CrossOrigin("localhost:4200")注释就可以了。
@CrossOrigin("http://localhost:4200")
public interface ConfigRepository extends JpaRepository<Config, String> {
    
}

在生产环境中:
如果您的应用程序正在运行在生产服务器上,您需要修改您的web.xml并添加一个(例如)内置CORS过滤器,以允许访问其他资源作为来源。
<!-- ==================== CORS Filter Definition ====================== -->

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>http://localhost:8080</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin</param-value>
    </init-param>
    <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Access-Control-Allow-Origin</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Tomcat 9 CORS Filter模板的区别在于将Access-Control-Allow-Origin添加到cors.allowed.header中,并在cors.allowed.origins中设置实际允许的来源,例如http://localhost:8080

因此,无需为HTTP请求指定显式标头。感谢@Neutrino。

0

使用此代码,根据您的结构进行自我管理。

app.use(
    cors({ 
        origin: http://localhost:4200, 
        methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
        allowedHeaders: [
            'Content-Type', 
            'Authorization', 
            'Origin', 
            'x-access-token', 
            'XSRF-TOKEN'
        ], 
        preflightContinue: false 
    })
);

app.get('/', (res, req, nxt) => {
    // only for adding cors on all requests
    nxt();
});

关于 Angular

constructor(private http: HttpClient) {
  this.root = 'http://localhost:8888';  //remove
  this.corsHeaders = new HttpHeaders({
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Access-Control-Allow-Origin': '/' .  // edit 
  });
  //this.contents = '';
 }

并使用<base> index.html


2
我根据您的建议进行了修改,但仍然出现相同的错误。而且,我不确定您所说的<base>在index.html中是什么意思。 - Abrar Hossain
1
<base>href改为什么? - Abrar Hossain
<base href="/"> - Chandan Kumar

0
为了解决这个问题,您可以为您的Angular应用程序创建一个代理。进入您的Angular项目目录,在src文件夹下创建一个名为proxy.config.json的文件。
proxy.config.json文件中:
{
  "/api/*": {
    "target": "http://localhost:3010",
    "secure": false,
    "logLevel": "debug"
  }
}

http://localhost:3010 是后端服务。

然后在 package.json 文件中,在 scripts 下添加这一行 "start": "ng serve --proxy-config proxy.config.json", 这将设置一个代理从你的应用到你的后端服务。

现在,当你进行任何 API 调用时,你不需要包含这部分: http://localhost:3010 例如,不要调用 http://localhost:3010/api/myapp,现在可以使用 /api/myapp

另外,在测试之前,请重新启动服务器。


-1

允许从Angular JS访问服务器端,这种情况下需要允许http://localhost:8888/,因为那是你的服务器端URL,Angular运行在http://localhost:4200上。请检查Http,然后它就可以工作了。


如果你使用 secure: false,那么就不需要使用 cors 中间件进行检查,然后也可以删除那段代码。这意味着从你的 Angular 中删除 cors 功能。我认为这不是一个好主意。 - Chandan Kumar
我同意关闭CORS不是一个好主意。 - Abrar Hossain
请查看以下链接以更好地理解 https://github.com/chandan4u/Node-JS-Security-Implementation/blob/master/server.js - Chandan Kumar
app.use(cors({ origin: http://localhost:4200, methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', allowedHeaders: ['Content-Type', 'Authorization', 'Origin', 'x-access-token', 'XSRF-TOKEN'], preflightContinue: false })); - Chandan Kumar
通过以下链接,我修改了我的cors实现并使用了您提供的实现。我替换了origin: ['http://localhost:4200']。但我仍然遇到了相同的错误。 - Abrar Hossain
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接