Angular CORS飞行前请求导致400错误请求,但邮递员请求有效

时间:2020-09-07 14:19:42

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

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

使用邮递员,我可以使用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标头。这不是问题(我不需要该标头),但这可能是一个线索。

第一个条目的“标题”面板显示: enter image description here

第二个条目显示: enter image description here

请注意,第二个Content-Type标头是text/html

在控制台中,我看到以下内容: enter image description here

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

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

2 个答案:

答案 0 :(得分: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应用于服务:

enter image description here

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

答案 1 :(得分:0)

尝试实现代理配置文件

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"
    }

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