【问题标题】:PUT Request fails HTTP preflight while GET and POST work (Using Angular 6 and Dotnet Core 2.1 Web API)PUT 请求在 GET 和 POST 工作时 HTTP 预检失败(使用 Angular 6 和 Dotnet Core 2.1 Web API)
【发布时间】:2018-09-27 04:43:28
【问题描述】:

我正在开发一个 Web 应用程序,前端使用 Angular 6,后端使用 .NET Core Web API。

我正在设置 API 以使用我的 Angular 服务。我的字体端和后端项目位于不同的本地主机端口上,但我已经在我的 API 中启用了 CORS 用于开发目的。

services.AddCors(o => o.AddDefaultPolicy(builder =>
{
    builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
}));

使用此设置,我可以成功执行 GET 请求。例如,我的 Angular 服务:

getAllFiles():Observable<MediaFile[]> {
  return this.http.get<MediaFile[]>(`${this.request_url}/files`)
}

可以调用我的 Web API 控制器:

[HttpGet]
public IEnumerable<MediaFile> GetFiles(string code = null)
{
    if (code != null)
    {
      return context.Files.Where(file => file.Meeting.Code == code);
    }
    return context.Files;
}

并且成功地给了我我请求的媒体文件。

为简洁起见,我不会在此处包含它,但我为 POST 做了同样的事情,它按预期工作。

我正在尝试为 PUT 请求添加处理程序。以下是相关代码:

putFile(file:MediaFile):Observable<MediaFile> {
  return this.http.put<MediaFile>(
    `${this.request_url}/files/${file.id}`, file
  )
}

在服务器端:

[HttpPut("{id}")]
public async Task<IActionResult> PutFile([FromRoute] int id, [FromBody] MediaFile file)
{
   if (!ModelState.IsValid)
   {
      return BadRequest(ModelState);
   }

   if (id != file.Id)
   {
      return BadRequest();
   }

   context.Entry(file).State = EntityState.Modified;

   try
   {
      await context.SaveChangesAsync();
   }
   catch (DbUpdateConcurrencyException)
   {
     if (!FileExists(id))
     {
       return NotFound();
     }
     else
     {
        throw;
      }
    }
    return NoContent();
}

但是当我尝试执行这段代码时,浏览器会发送一个 HTTP OPTIONS 请求。一些谷歌搜索告诉我,这很可能是preflight request。然后它发现我的 API 中不存在 OPTIONS 方法,并返回 404。

我不明白为什么在这种情况下预检请求会失败。为什么我的 API 适用于 GET 和 POST 请求,但不适用于 PUT?有人可以解释一下这里可能出了什么问题。

谢谢!

【问题讨论】:

    标签: javascript http asp.net-web-api .net-core angular6


    【解决方案1】:

    我设法通过手动捕获预检请求并返回响应来解决此问题:

    [HttpOptions("{id}")]
    public IActionResult HandleOptions([FromRoute] int id)
    {
          Request.HttpContext.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
          Request.HttpContext.Response.Headers.Add("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with");
          Request.HttpContext.Response.Headers.Add("Access-Control-Max-Age", "3600");
          return Ok();
     }
    

    这感觉有点像 hack,但现在它满足了我的需求。如果有人能阐明真正的问题可能是什么,我仍然会很感激。

    【讨论】:

      【解决方案2】:

      显然这是使用 Windows 身份验证 (NTLM) 和使用 cors 时 asp.net core 中的一个错误。当浏览器发送预检OPTIONS请求时,它不应该要求认证,但是服务器处理它就像需要认证一样,导致401错误。

      我从这里找到并修改了一个解决方案:https://github.com/e5Workflow/CoreWebApp

      那里的解决方案显然有点过时,因为它在我的 aspnetcore 2.1 应用程序中不起作用,但我从代码中挑选了相关的部分并使它起作用。

      1. 通过编辑launchsettings.json启用匿名身份验证

        "windowsAuthentication": true,
        "anonymousAuthentication": true,
        
      2. startup.cs 中添加以下作为您的第一个中间件:

        app.Use(async (context, next) =>
        {
          if(context.Request.Method != "OPTIONS" && !context.User.Identity.IsAuthenticated)
          {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Not Authenticated");
          }
          else
          {
            await next.Invoke();
          }
        });
        

      这个想法是除了OPTIONS 请求之外的每个请求仍然需要身份验证,但OPTIONS 请求不需要,因此正确的200 响应和标头将被发回。

      【讨论】:

      • 感谢您的建议。不幸的是,当我尝试这个时,它只是让每个请求都给我一个 401... 仍然使用我现在发布的 hack。
      • 嗯,我想知道为什么?我在生产中使用它没有问题。你在使用 NTLM 身份验证吗?
      猜你喜欢
      • 2015-05-17
      • 2018-05-14
      • 2020-01-28
      • 2019-09-08
      • 2021-10-03
      • 2018-04-22
      • 2015-06-21
      • 1970-01-01
      • 2021-11-19
      相关资源
      最近更新 更多