【问题标题】:Angular CORS preflight request causes 400 Bad Request, but Postman request works OKAngular CORS 预检请求导致 400 Bad Request,但 Postman 请求工作正常
【发布时间】:2025-11-26 13:15:02
【问题描述】:

我有一个 Angular 应用程序在我的开发机器上本地运行,它试图向另一台机器上的 IIS 上运行的 WCF Web 服务发出请求。

使用 Postman,我可以使用 POST 和 OPTIONS 动词向服务发出请求。

但是,当我的 Angular 应用发出请求时,我在发出的 CORS 预检 OPTIONS 请求中出现 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

如果我检查这两个条目的请求标头,我也看不到我的代码尝试添加的 Test 标头。这不是问题(我不需要那个标题),但它可能是一个线索。

第一个条目的“标题”面板显示:

对于第二个条目,它显示:

请注意,第二个标题中的Content-Typetext/html

在控制台中我看到:

我没有在我的 Web 服务日志中看到任何条目,也没有在 IIS 日志或服务器上的事件查看器中看到任何错误。

什么可能导致此问题,我该如何解决它或获取更多信息?

【问题讨论】:

  • 您的后端似乎没有正确响应 OPTION 请求(400 错误请求)。它需要以 200 和正确的 CORS 标头进行响应..
  • 我可以使用 Postman 的 OPTIONS 请求并获得 200 响应,那么为什么 Angular 应用程序不同?

标签: angular wcf cors http-status-code-400


【解决方案1】:

这是由 CORS 引起的。不要使用全局文件来解决跨域问题。可以尝试使用 IDispatchMessageInspector 解决跨域问题。

将 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 应用到服务:

如果问题仍然存在,请随时告诉我。

【讨论】:

  • 是的,它成功了 - 非常感谢!但是,我仍然不清楚如何解决它,但是将 CORS 标头放入配置中并没有,以及为什么来自 Postman 的 OPTIONS 请求有效但我的 Angular 应用程序没有。还有一些我不明白,但我不知道它是什么。
【解决方案2】:

尝试实现代理配置文件

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-&gt;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"
    }

更多信息可以找到here in the official angular documentation

【讨论】:

    最近更新 更多