简体   繁体   English

ASP.NET 5 Web API CreatedAtAction:POST 返回 500

[英]ASP.NET 5 Web API CreatedAtAction: POST returns 500

I have a simple ASP.NET 5 Web API with a controller for uploading/downloading files, like:我有一个简单的 ASP.NET 5 Web API,带有一个用于上传/下载文件的控制器,例如:

[Authorize(Roles = "admin,browser,writer")]
[HttpPost("api/contents/{id}")]
public IActionResult UploadContent(IFormFile file,
    [FromForm] string mimeType,
    [FromForm] string id)
{
  // store...
  return CreatedAtAction(nameof(DownloadContent), new
  {
      id = id
  });
}

[Authorize]
[HttpGet("api/contents/{id}")]
public FileResult DownloadContent([FromRoute] string id)
{
    // fetch into item...
    return File(item.Content, item.MimeType);
}

The upload action works fine, until the method returns: at this point, when the framework handles the serialization for the 201 return object (which should set its location to the corresponding download action) I get an InvalidOperationException telling that No route matches the supplied values .上传操作正常工作,直到该方法返回:此时,当框架处理 201 返回对象的序列化(应将其位置设置为相应的下载操作)时,我得到一个InvalidOperationException告诉我No route matches the supplied values .

I found this issue about a similar problem, but none of the mentioned solutions seem to work.我发现 这个问题是关于一个类似的问题,但提到的解决方案似乎都不起作用。 I tried:我试过了:

  1. explicitly naming the download action with [ActionName("DownloadContent")] , even though this should not make any difference;使用[ActionName("DownloadContent")]显式命名下载操作,即使这应该没有任何区别; this appears to be relevant only when the action name ends with Async (see this post , and the official issue ).这似乎仅在操作名称以Async结尾时才相关(请参阅此帖子和官方问题)。
  2. use CreatedAtRoute instead, by adding the corresponding Name="DownloadContent" in the HttpGet attribute of DownloadContent .使用CreatedAtRoute代替,通过加入相应的Name="DownloadContent"中的属性HTTPGET DownloadContent
  3. use CreatedAtActionResult like:使用CreatedAtActionResult像:
return new CreatedAtActionResult(nameof(DownloadContent),
    nameof(ItemContentController), new { id = id }, null);
  1. directly create the URL and return a CreatedResult object:直接创建 URL 并返回一个CreatedResult对象:
string url = Url.Action(new UrlActionContext
{
    Protocol = Request.Scheme,
    Host = Request.Host.Value,
    Action = nameof(DownloadContent)
});
return new CreatedResult(url, null);

Now, that's the point: the above code returns null for the url , and this then makes CreatedResult throw as the url for the 201 header's Location is null.现在,这就是重点:上面的代码为url返回null ,然后这使CreatedResult抛出,因为 201 标头的Locationurl为 null。 If I change this code by selecting a different action, eg Action = nameof(UploadContent) , it works, and I get the URL for the action.如果我通过选择不同的操作来更改此代码,例如Action = nameof(UploadContent) ,它会起作用,并且我会获得该操作的 URL。

For completeness, my Startup.cs Configure method is like this:为了完整Startup.cs ,我的Startup.cs Configure方法是这样的:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor
            | ForwardedHeaders.XForwardedProto
    });
    if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
    else
    {
        app.UseExceptionHandler("/Error");
        if (Configuration.GetValue<bool>("Server:UseHSTS")) app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseCors("CorsPolicy");
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
    // ... Swagger stuff ...
}

When using overload CreatedAtAction(string, object) , param object is not route parameters as you assume, but body content.使用重载CreatedAtAction(string, object) ,param object不是您假设的路由参数,而是正文内容。 Hence, code is looking for a route to GET api/contents which doesn't exists.因此,代码正在寻找一条不存在的 GET api/contents路线。

You'll need to use overload CreatedAtAction(string, object, object) where param #2 contains route params, and param #3 contains body content.您需要使用重载CreatedAtAction(string, object, object)其中 param #2 包含路由参数,而 param #3 包含正文内容。 So in your case, return CreatedAtAction(nameof(DownloadContent), new { id = id }, null);所以在你的情况下, return CreatedAtAction(nameof(DownloadContent), new { id = id }, null); will work.将工作。

I was able to reproduce your issue with this controller and Postman:我能够使用此控制器和邮递员重现您的问题:

public class MyContentController : ControllerBase
{
    [HttpGet("api/contents/{id}")]
    public IActionResult DownloadContent(int id)
    {
        return Ok(1);
    }
    
    [HttpPost("api/contents/{id}")]
    public IActionResult UploadContent(int id)
    {
        return CreatedAtAction(nameof(DownloadContent), new { id = id });
    }
}

I got expected response when replacing更换时我得到了预期的响应

return CreatedAtAction(nameof(DownloadContent), new { id = id });

with

return CreatedAtAction(nameof(DownloadContent), new { id = id }, null);

Doc: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.createdataction?view=aspnetcore-5.0#Microsoft_AspNetCore_Mvc_ControllerBase_CreatedAtAction_System_String_System_Object _文档: https : //docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.createdataction? view = aspnetcore-5.0# Microsoft_AspNetCore_Mvc_ControllerBase_CreatedAtAction_System_String_System_Object _

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM