英文:
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!");
});
我尝试了启用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
英文:
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
答案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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论