Angular CORS 预检请求导致 400 Bad Request,但 Postman 请求正常
Angular CORS preflight request causes 400 Bad Request, but Postman request works OK
我的开发机器上本地有一个 Angular 应用 运行,它试图向另一台机器上 IIS 上的 WCF Web 服务 运行 发出请求。
使用 Postman,我可以使用 POST 和 OPTIONS 动词向服务发出请求。
但是,当我的 Angular 应用发出请求时,我最终收到来自发出的 CORS 预检选项请求的 400 错误。
请求是这样的:
// passed-in url is https://myserver/myservice/Hello
public hello( url: string, name: string )
{
const headers = new HttpHeaders()
headers.append('Content-Type', 'application/json');
headers.append('Test', 'TestyTest');
return this.httpClient.post<string>( url,
{ name: name },
{ headers: headers }
);
}
在 Chrome 的网络面板调试工具中,我看到这个请求有两个条目:
Hello (failed) xhr zone.js:2935 0 B 188 ms
Hello 400 text/html Other 0 B 176 ms
如果我检查这两个条目的请求 header,我都没有看到我的代码试图添加的 Test
header。这不是问题(我不需要那个 header),但这可能是一个线索。
第一个条目的“Headers”面板显示:
第二个条目显示:
请注意,第 2 个 Content-Type
header 是 text/html
。
在控制台中我看到了这个:
我在我的 Web 服务日志中没有看到任何条目,或者在 IIS 日志或服务器上的事件查看器中没有看到任何错误。
是什么导致了这个问题,我该如何解决或获取更多信息?
尝试实施代理配置文件
const HttpsProxyAgent = require('https-proxy-agent');
/*
* API proxy configuration.
* This allows you to proxy HTTP request like `http.get('/api/stuff')` to another server/port.
* This is especially useful during app development to avoid CORS issues while running a local server.
* For more details and options, see https://angular.io/guide/build#using-corporate-proxy
*/
const proxyConfig = [
{
context: '/api',
pathRewrite: { '^/api': '' },
target: 'https://api.chucknorris.io',
changeOrigin: true,
secure: false,
},
];
/*
* Configures a corporate proxy agent for the API proxy if needed.
*/
function setupForCorporateProxy(proxyConfig) {
if (!Array.isArray(proxyConfig)) {
proxyConfig = [proxyConfig];
}
const proxyServer = process.env.http_proxy || process.env.HTTP_PROXY;
let agent = null;
if (proxyServer) {
console.log(`Using corporate proxy server: ${proxyServer}`);
agent = new HttpsProxyAgent(proxyServer);
proxyConfig.forEach((entry) => {
entry.agent = agent;
});
}
return proxyConfig;
}
module.exports = setupForCorporateProxy(proxyConfig);
接下来,打开您的 angular.json
文件并在 serve->options
下添加一个 proxyConfig 键,指向您刚刚创建的 src/proxy.conf.json
文件,如下所示:
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
}
这是由 CORS 引起的。不要使用全局文件来解决 cross-domain 问题。您可以尝试使用 IDispatchMessageInspector 来解决 cross-domain 个问题。
向您的项目添加一个 SOAP.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
namespace Demo_rest_ConsoleApp
{
public class ServerMessageLogger : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
Console.WriteLine(request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
if (ctx.IncomingRequest.Method == "OPTIONS")
{
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,POST,DELETE,OPTIONS");
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
}
}
}
public class ClientMessageLogger : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false)]
public class CustContractBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
{
public Type TargetContract => throw new NotImplementedException();
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new ServerMessageLogger());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
}
最后,将CustContractBehavior应用到服务中:
如果问题仍然存在,请随时告诉我。
我的开发机器上本地有一个 Angular 应用 运行,它试图向另一台机器上 IIS 上的 WCF Web 服务 运行 发出请求。
使用 Postman,我可以使用 POST 和 OPTIONS 动词向服务发出请求。
但是,当我的 Angular 应用发出请求时,我最终收到来自发出的 CORS 预检选项请求的 400 错误。
请求是这样的:
// passed-in url is https://myserver/myservice/Hello
public hello( url: string, name: string )
{
const headers = new HttpHeaders()
headers.append('Content-Type', 'application/json');
headers.append('Test', 'TestyTest');
return this.httpClient.post<string>( url,
{ name: name },
{ headers: headers }
);
}
在 Chrome 的网络面板调试工具中,我看到这个请求有两个条目:
Hello (failed) xhr zone.js:2935 0 B 188 ms
Hello 400 text/html Other 0 B 176 ms
如果我检查这两个条目的请求 header,我都没有看到我的代码试图添加的 Test
header。这不是问题(我不需要那个 header),但这可能是一个线索。
第一个条目的“Headers”面板显示:
第二个条目显示:
请注意,第 2 个 Content-Type
header 是 text/html
。
在控制台中我看到了这个:
我在我的 Web 服务日志中没有看到任何条目,或者在 IIS 日志或服务器上的事件查看器中没有看到任何错误。
是什么导致了这个问题,我该如何解决或获取更多信息?
尝试实施代理配置文件
const HttpsProxyAgent = require('https-proxy-agent');
/*
* API proxy configuration.
* This allows you to proxy HTTP request like `http.get('/api/stuff')` to another server/port.
* This is especially useful during app development to avoid CORS issues while running a local server.
* For more details and options, see https://angular.io/guide/build#using-corporate-proxy
*/
const proxyConfig = [
{
context: '/api',
pathRewrite: { '^/api': '' },
target: 'https://api.chucknorris.io',
changeOrigin: true,
secure: false,
},
];
/*
* Configures a corporate proxy agent for the API proxy if needed.
*/
function setupForCorporateProxy(proxyConfig) {
if (!Array.isArray(proxyConfig)) {
proxyConfig = [proxyConfig];
}
const proxyServer = process.env.http_proxy || process.env.HTTP_PROXY;
let agent = null;
if (proxyServer) {
console.log(`Using corporate proxy server: ${proxyServer}`);
agent = new HttpsProxyAgent(proxyServer);
proxyConfig.forEach((entry) => {
entry.agent = agent;
});
}
return proxyConfig;
}
module.exports = setupForCorporateProxy(proxyConfig);
接下来,打开您的 angular.json
文件并在 serve->options
下添加一个 proxyConfig 键,指向您刚刚创建的 src/proxy.conf.json
文件,如下所示:
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
}
这是由 CORS 引起的。不要使用全局文件来解决 cross-domain 问题。您可以尝试使用 IDispatchMessageInspector 来解决 cross-domain 个问题。
向您的项目添加一个 SOAP.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
namespace Demo_rest_ConsoleApp
{
public class ServerMessageLogger : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
Console.WriteLine(request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
if (ctx.IncomingRequest.Method == "OPTIONS")
{
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,POST,DELETE,OPTIONS");
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
}
}
}
public class ClientMessageLogger : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false)]
public class CustContractBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
{
public Type TargetContract => throw new NotImplementedException();
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new ServerMessageLogger());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
}
最后,将CustContractBehavior应用到服务中:
如果问题仍然存在,请随时告诉我。