Nest.js/Angular – 启用CORS但仍然在发出请求时收到CORS错误

huangapple go评论77阅读模式
英文:

Nest.js/Angular - CORS Enabled but still getting a CORS error while making requests

问题

我正在构建一个小型计算器应用程序,以学习Nest.js和Angular。我已经设置了一个托管简单Web API的服务器,其中包含一些端点,其中之一返回后端当前支持的所有操作码的列表。我正在尝试在前端初始化时获取并使用此列表来填充下拉选择器。

前端

calculator-app/src/app/calculator-form/ 包含消费请求的表单组件。它只是调用 OpcodeService.getSupportedOpcodes() 并记录结果。此时,我知道我需要对结果对象进行更多处理,但由于CORS错误,我还没有进展。

import { Component } from '@angular/core';
import { OpcodeService } from '../services/opcode.service';

@Component({
  selector: 'app-calculator-form',
  templateUrl: './calculator-form.component.html',
  styleUrls: ['./calculator-form.component.scss'],
  providers: [OpcodeService]
})
export class CalculatorFormComponent {
  supportedOpcodes: string[] = ['?'];

  constructor(private opcodeService: OpcodeService) {}

  ngOnInit() {
    this.opcodeService.getListOfSupportedOpcodes().subscribe(res => {
      console.log(res);
    });
  }
}

calculator-app/src/app/services/opcode.service.ts 发起实际的HTTP请求。当我使用Postman进行测试时,此端点按预期工作,我尽力模拟了在Postman中有效的标头。这也相当简单,但可能我在这里漏掉了一些配置。

import { Injectable } from '@angular/core';
import { Environment } from '../environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class OpcodeService {
  supportedOpcodes: string[] = [];

  constructor(private http: HttpClient) {}

  getListOfSupportedOpcodes() {
    const headers = new HttpHeaders()
      .set('content-type', 'application/x-www-form-urlencoded')
      .set('Access-Control-Allow-Origin', '*');

    return this.http.get(Environment.serverUrl + "/opcode", {'headers': headers});
  }
}

后端

我相当有信心我的路由没有问题。如上所述,我能够如预期地从 GET -> localhost:3000/opcode 看到返回的值。所以这是我的 main.ts 文件,带有 enableCors() 配置对象。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    allowedHeaders: ['content-type'],
    origin: 'http://localhost:4200/'
  });
  await app.listen(3000);
}

bootstrap().then(function() {
  console.log("\nReady for maths!");
});

Chrome中CORS错误的屏幕截图

我尝试了启用CORS以及允许的标头。我知道Nest.js默认情况下有问题解析form-data,所以我尝试设置GET请求的标头。尝试按照此帖子中的说明设置来源。

这是完整的存储库。欢迎提出任何其他反馈意见的问题!

英文:

I am building a little calculator app to learn Nest.js and Angular. I have set up a server hosting a simple web API with a few endpoints, one of which returns a list of all the currently supported Opcodes on the backend. I am trying to fetch (on init) and consume this list to populate a dropdown selector on the frontend.

Frontend

calculator-app/src/app/calculator-form/ holds the form component that consumes the request. All it is doing is calling OpcodeService.getSupportedOpcodes() and logging the result. At this point, I know I'll need to do more work with the result object, but because of the CORS error I haven't gotten that far.

import { Component } from '@angular/core';
import { OpcodeService } from '../services/opcode.service';

@Component({
  selector: 'app-calculator-form',
  templateUrl: './calculator-form.component.html',
  styleUrls: ['./calculator-form.component.scss'],
  providers: [ OpcodeService ]
})
export class CalculatorFormComponent {
  supportedOpcodes: string[] = ['?']

  constructor(private opcodeService: OpcodeService) {}

  ngOnInit() {
    this.opcodeService.getListOfSupportedOpcodes().subscribe(res => {
      console.log(res);
    })
  }
}

calculator-app/src/app/services/opcode.service.ts makes the actual HTTP request. This endpoint works as expected when I test with Postman, and I did my best to emulate the header that worked with postman. This is also fairly simple, but its possible I'm missing some configuration here.

import { Injectable } from '@angular/core';
import { Enviornment } from '../enviornment';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  // declares that this service should be created
  // by the root application injector.
  providedIn: 'root'
})
export class OpcodeService {
  supportedOpcodes: string[] = [];

  constructor(private http: HttpClient) {}

  getListOfSupportedOpcodes() {
    const headers = new HttpHeaders()
      .set('content-type', 'application/x-www-form-urlencoded')
      .set('Access-Control-Allow-Origin', '*');
    
    return this.http.get(Enviornment.serverUrl + "/opcode", {'headers':  headers});
  }
}



Backend

I'm reasonably confident there is nothing wrong with my routing. As mentioned above I was able to see the values returning as expected from GET -> localhost:3000/opcode. So here is my main.ts file, with the enableCors() configuration object.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    allowedHeaders: ['content-type'],
    origin: 'http://localhost:4200/'
  });
  await app.listen(3000);
}

bootstrap().then(function() {
  console.log("\nReady for maths!");
}); 

Screen capture of CORS error in Chrome

I have tried enabling CORS with the allowed headers. I know that Nest.js has issues parsing form-data by default, so I tried setting the headers of the GET request. Tried setting the origin following this post.

Here is the full repository. Feel free to open issues with any other feedback!

答案1

得分: 0

确保你尝试访问的URL以 http://https:// 开头。

英文:

Make sure that the URL that you're trying to hit has http:// or https:// at the begining.

答案2

得分: 0

要在本地前端访问它,您需要更改main.ts中的CORS设置。

app.enableCors({
  allowedHeaders: ['content-type'],
  origin: 'http://localhost:4200/'
});

app.enableCors(); //不带任何配置选项。

Main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
 const app = await NestFactory.create(AppModule);
 app.enableCors();
 await app.listen(3000);
}

bootstrap().then(function() {
 console.log("\nReady for maths!");
});

为了正确处理它,我们可以根据环境设置保持这个设置,以使它成为可用于生产的代码。CORS设置也可以保存在托管云服务的应用程序配置中。

if (process.env.ENV === 'development' && process.env.LOCAL_ENV === 'enabled') {
  app.enableCors();
}

来自Angular应用程序本地主机的工作请求:http://localhost:4200

来自Angular应用程序本地主机的工作请求

英文:

To access it from the front end locally you will have to change the main.ts enabled CORS settings.

From

app.enableCors({
  allowedHeaders: ['content-type'],
  origin: 'http://localhost:4200/'
});

To

app.enableCors(); //without any configuration option.

Main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
 const app = await NestFactory.create(AppModule);
 app.enableCors();
 await app.listen(3000);
}

 bootstrap().then(function() {
 console.log("\nReady for maths!");
}); 

To handle it properly, we can keep this setting base on the environments settings to make this production usable code. The CORS setting can be kept in the application configuration on the hosting could service as well.

if (process.env.ENV === 'development' && process.env.LOCAL_ENV === 
'enabled') {
  app.enableCors();
 }

Working request from the angular app localhost: http://localhost:4200

Working request from the angular app local host

答案3

得分: 0

我还要补充的是,opcode.service.ts 中的额外头部是不必要的。这个解释和类比是由我的GitHub上的Omar提供的。我将会再次将存储库设置为私有,所以我也会在这里粘贴它:

回答

Access-Control-Allow-Origin 和 allow-origins 这些头部应该在服务器端配置,而不是客户端。

想象一下,你是一家酒吧的保安,你有一个政策,只允许特定地址的人进入。现在,想象一个顾客走近酒吧,请求让他们进入。他们出示了身份证明(握手头部),其中包括请求进入的备注(这个备注代表了在该方法中提供的客户端头部)。然而,作为保安,你明白最终是酒吧决定谁可以进入,而不是顾客的决定。

我建议如果你需要为每个调用使用头部,可以寻找一个 Interceptor,如果不需要,就越简单越好。

如果你需要发送一个 x-www-form-urlencoded 调用,可以根据需要发送一个 FormData 对象。像这样:

let formData = new FormData();
formData.append('parameter', 'value');
return this.http.post(URL, formData);
英文:

I'll also add here that the extra headers in opcode.service.ts are not necessary. This explanation and analogy was given by Omar on my GitHub. I'll be making the repository private again, so I'll paste it here as well:

Answer

The headers Access-Control-Allow-Origin and allow-origins should be configured on the server side, not the client side.

Imagine that you are a bouncer at a bar and you have a policy that only allows entry to people from a specific address. Now, picture a scenario where a customer approaches the bar and asks you to let them in. They show you their identification (the handshake headers), which includes a note requesting entry (this note represents the client-supplied headers that were in that method). However, as the bouncer, you understand that it is ultimately the bar's decision to determine who gets to enter, not the customer's

I recommend you that if you need to use headers for every single call that you're making, look for an Interceptor, if not, the simpler the better.

If you need to send a x-www-form-urlencoded call, you can send a FormData object if needed. Like this:

let formData = new FormData();
formData.append('parameter', 'value');
return this.http.post(URL, formData);

huangapple
  • 本文由 发表于 2023年7月10日 10:36:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76650369.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定