【发布时间】:2021-08-26 21:56:46
【问题描述】:
我正在尝试从我的 Angular 前端向 Canada Posts Get Rate API 发出请求。不幸的是,我的后端 .Net Core 2.2 似乎阻止了所有预检请求。如果我尝试使用禁用网络安全标志的 chrome 浏览器发出这些请求,我可以完成请求,但不幸的是,在正常浏览时我无法完成请求。我已尝试在这里阅读许多线程,并在 .NET 文档中指定了 cors 异常,但似乎预检请求仍被阻止。
错误信息: 从源“http://localhost:4200”访问“https://soa-gw.canadapost.ca/rs/ship/price”的 XMLHttpRequest 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。
我引用的一些文档没有成功,我尝试向 C# 添加额外的 cors 权限,但预检请求仍然被阻止。 https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-5.0 https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#how-cors-works
我也无法修改前端 http 标头以删除预检检查,因为其中一些是加拿大邮政 api 所要求的。 https://origin-www.canadapost.ca/info/mc/business/productsservices/developers/services/rating/getrates/default.jsf
前端调用
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/vnd.cpc.ship.rate-v4+xml', //<- To SEND XML
'Accept': 'application/vnd.cpc.ship.rate-v4+xml', //<- To ask for XML //<- b/c Angular understands text
'Authorization': 'Basic YTUxZGQ1MWY2YjJhNjE4ODowODgzOThiZTUxY2Y4Y2RjZTIzM34',
'Accept-language': 'en-CA',
'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Methods": "DELETE, POST, GET, OPTIONS",
}),
responseType: 'text' as 'json'
};
var postedData = `
<mailing-scenario xmlns="https://www.canadapost.ca/ws/ship/rate-v4">
<customer-number>0009669089</customer-number>
<parcel-characteristics>
<weight>5 </weight>
</parcel-characteristics>
<origin-postal-code>M5V0G1</origin-postal-code>
<destination>
<domestic>
<postal-code>M4R1L8</postal-code>
</domestic>
</destination>
</mailing-scenario>
`;
// http post to canada post using angular httpclient
this.http.post('https://soa-gw.canadapost.ca/rs/ship/price', postedData, httpOptions)
.subscribe(
result => {
console.log(result);
},
error => console.log('There was an error: ', error));
后端启动.cs
using AutoMapper;
using Domain;
using Domain.Entities;
using Domain.Entities.Projects;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Rolox.Domain.Entities;
using Server.Classes;
using Server.Interfaces;
using Server.Models;
using System;
using System.IO;
namespace Server
{
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("*")
.AllowAnyHeader()
.AllowAnyMethod();
});
options.AddPolicy("MyAllowAllHeadersPolicy",
builder =>
{
builder.WithOrigins("*").AllowAnyMethod()
.AllowAnyHeader();
});
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSwaggerDocument();
services.AddDbContext<RoloxDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
providerOptions => providerOptions.EnableRetryOnFailure()));
services.AddDefaultIdentity<User>()
.AddRoles<Role>()
.AddEntityFrameworkStores<RoloxDbContext>();
/// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
ConfigureRepositories(services);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.Configure<SettingModel>(Configuration.GetSection("Settings"));
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
});
services.Configure<FormOptions>(options =>
{
options.MemoryBufferThreshold = Int32.MaxValue;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("MyAllowAllHeadersPolicy");
app.UseOpenApi();
app.UseSwaggerUi3();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
});
}
private void ConfigureRepositories(IServiceCollection services)
{
/// Repository Injections
services.AddScoped<IRoloxRepository<ProposalStage>, ProposalStageRepository>();
services.AddScoped<IProposalRepository, ProposalRepositoy>();
services.AddScoped<IRoloxRepository<ChecklistItem>, ChecklistRepository>();
services.AddScoped<IRoloxRepository<Contractor>, ContractorRepository>();
services.AddScoped<IRoloxRepository<Project>, ProjectRepository>();
services.AddScoped<IDirectCostRepository, ProjectDirectCostRepository>();
services.AddScoped<IInvoiceRepository, InvoiceRepositoy>();
services.AddScoped<ICompanyProfileRepository, CompanyRepository>();
}
}
}
解决方案:从生产中发送消息,前端预检请求被阻止,而是从后端服务器发送 api 调用。尽管在实时使用中禁用网络安全进行测试,但它必须从后端发送。
【问题讨论】:
-
除非您的服务器域名是“soa-gw.canadapost.ca”,否则CORS错误与您的ASP.NET服务器配置无关。此外,CORS 标头(“Access-Control-Allow-”)是 *response 标头,而不是请求标头,因此将它们包含在请求中没有好处。
标签: c# angular .net-core cors .net-core-2.2