在 .Net Core 2.2 中将 HttpResponseMessage 作为 IActionResult 返回的正确方法
Posted
技术标签:
【中文标题】在 .Net Core 2.2 中将 HttpResponseMessage 作为 IActionResult 返回的正确方法【英文标题】:Correct way to return HttpResponseMessage as IActionResult in .Net Core 2.2 【发布时间】:2019-06-05 18:48:17 【问题描述】:在 .Net Core 2.2 中。我正在创建一个 API 控制器,该控制器根据有效负载将请求路由到另一个 Http 端点。
[Route("api/v1")]
public class RoutesController : Controller
[HttpPost]
[Route("routes")]
public async Task<IActionResult> Routes([FromBody]JObject request)
var httpClient = new HttpClient();
// here based on request httpCLient will make `POST` or `GET` or `PUT` request
// and returns `Task<HttpResponseMessage>`. Lets assume its making `GET`
// call
Task<HttpResponseMessage> response = await
httpClient.GetAsync(request["resource"]);
/* ??? what is the correct way to return response as `IActionResult`*/
基于SO 帖子我可以做到这一点
return StatusCode((int)response.StatusCode, response);
但是我不确定将HttpResponseMessage
作为ObjectResult
发送是正确的方式。
我还想确保内容协商能够奏效。
【问题讨论】:
你不能像以前的 web api 框架那样返回一个 HttpResponseMessage 对象。相反,您可以创建一个自定义 IActionResult(如 HttpResponseMessageResult),它将状态码、标头和正文复制到 ActionResult 的 ExecuteResultAsync 方法中的 httpContext.Response 有什么例子吗?后端 http api 可能会返回 json 结果或流。所以HttpResponseMessage
的信息将是HttpContent
你能解决这个问题吗?
是的,我在***.com/a/54187518/3862378下方发布了我的答案
相关帖子 - Convert from HttpResponseMessage to IActionResult in .NET Core
【参考方案1】:
您可以创建一个自定义 IActionResult 来包装传输逻辑。
public async Task<IActionResult> Routes([FromBody]JObject request)
var httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync("");
// Here we ask the framework to dispose the response object a the end of the user resquest
this.HttpContext.Response.RegisterForDispose(response);
return new HttpResponseMessageResult(response);
public class HttpResponseMessageResult : IActionResult
private readonly HttpResponseMessage _responseMessage;
public HttpResponseMessageResult(HttpResponseMessage responseMessage)
_responseMessage = responseMessage; // could add throw if null
public async Task ExecuteResultAsync(ActionContext context)
context.HttpContext.Response.StatusCode = (int)_responseMessage.StatusCode;
foreach (var header in _responseMessage.Headers)
context.HttpContext.Response.Headers.TryAdd(header.Key, new StringValues(header.Value.ToArray()));
if(_responseMessage.Content != null)
using (var stream = await _responseMessage.Content.ReadAsStreamAsync())
await stream.CopyToAsync(context.HttpContext.Response.Body);
await context.HttpContext.Response.Body.FlushAsync();
【讨论】:
我在第 523 行查看 github.com/aspnet/Mvc/blob/release/2.2/src/… 和 github.com/aspnet/Mvc/blob/release/2.2/src/…。不确定那会起作用。它基本上返回HttpReponseMessage
作为 ObjectResult 的 Response
属性。我也试试你的方法
在 webapishim 中,他们使用您可能需要在某处注册的自定义格式化程序 (github.com/aspnet/Mvc/blob/release/2.2/src/…)。这是一个比我的更完整的实现。
是的,但由于它是开源的,您可以从所需的部分中获得灵感,而无需其他所有兼容性层。
我使用了您创建 IActionResult 的方法,但是我使用了 github.com/aspnet/Mvc/blob/release/2.2/src/… 中的代码 ExecuteResultAsync
.. 请参阅下面的帖子
不适用于以下场景。假设您有 2 个 Web API,(A) 和 (B)。 (A) 中的端点调用 (B) 然后 (B) 调用不同的端点但也在 (A) 中。 (B) 回复 (A)。 (A) 无法处理来自 (B) 的响应【参考方案2】:
public class HttpResponseMessageResult : IActionResult
private readonly HttpResponseMessage _responseMessage;
public HttpResponseMessageResult(HttpResponseMessage responseMessage)
_responseMessage = responseMessage; // could add throw if null
public async Task ExecuteResultAsync(ActionContext context)
var response = context.HttpContext.Response;
if (_responseMessage == null)
var message = "Response message cannot be null";
throw new InvalidOperationException(message);
using (_responseMessage)
response.StatusCode = (int)_responseMessage.StatusCode;
var responseFeature = context.HttpContext.Features.Get<IHttpResponseFeature>();
if (responseFeature != null)
responseFeature.ReasonPhrase = _responseMessage.ReasonPhrase;
var responseHeaders = _responseMessage.Headers;
// Ignore the Transfer-Encoding header if it is just "chunked".
// We let the host decide about whether the response should be chunked or not.
if (responseHeaders.TransferEncodingChunked == true &&
responseHeaders.TransferEncoding.Count == 1)
responseHeaders.TransferEncoding.Clear();
foreach (var header in responseHeaders)
response.Headers.Append(header.Key, header.Value.ToArray());
if (_responseMessage.Content != null)
var contentHeaders = _responseMessage.Content.Headers;
// Copy the response content headers only after ensuring they are complete.
// We ask for Content-Length first because HttpContent lazily computes this
// and only afterwards writes the value into the content headers.
var unused = contentHeaders.ContentLength;
foreach (var header in contentHeaders)
response.Headers.Append(header.Key, header.Value.ToArray());
await _responseMessage.Content.CopyToAsync(response.Body);
【讨论】:
不知道为什么你得到了 -1,这确实有效,除了在我的 .net core 2.2 项目中将Headers.Append
更改为 Headers.TryAdd
只是想添加一个评论,这在 .Net core 3.1 中有效,而不是接受的答案。
它适用于核心 3.1!如果您添加using Microsoft.AspNetCore.Http;
,它将解析Headers.Append
扩展方法。
有更好的方法吗?不使用那个“未使用”的变量?
@Thor88 使用丢弃标识符来消除未使用的变量警告,即var _ = contentHeaders.ContentLength;
【参考方案3】:
ASP.NET Core 有返回对象RedirectResult 来重定向调用者。
【讨论】:
首先,您提供的链接实现了IHttpActionResult
而不是IActionResult
。我知道 .net 核心有相同的对象。但是重定向用于将用户重定向到特定的 url。我的问题与 API 而非网站有关。您不会从 API 重定向
我记错了 IHttpActionResult 实现了 IActionResult。在这种情况下,RedirectResult 就是你想要的。
如何重定向 POST 请求?还是在不向用户发送凭据的情况下管理经过身份验证的请求?
RedirectResult 不支持 POST 请求吗?我不确定你在第二个问题中的意思。
有时您要转发的资源需要您不能发送给请求用户的凭据。在这种情况下,您不能发送重定向响应,而是充当代理服务器。其他用例,目标资源位于用户无法访问的网络中。【参考方案4】:
只需将响应包装在 Ok() 操作返回类型中:
return Ok(response)
所以你的代码看起来像:
[Route("api/v1")]
public class RoutesController : Controller
[HttpPost]
[Route("routes")]
public async Task<IActionResult> Routes([FromBody]JObject request)
var httpClient = new HttpClient();
Task<HttpResponseMessage> response = await httpClient.GetAsync(request["resource"]);
return Ok(response);
更多信息在这里:https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1
【讨论】:
以上是关于在 .Net Core 2.2 中将 HttpResponseMessage 作为 IActionResult 返回的正确方法的主要内容,如果未能解决你的问题,请参考以下文章
在 Visual Studio 2017 中将 .NET Core 2.2 应用程序发布为依赖于框架的可执行文件 (FDE)
在 Razor (chtml) 中渲染动态视图,如何在 asp.net core 3.0 中将 FileProvider 添加到 razor?
如何在 Visual Studio 中将 .NET Framework 更改为 .NET Standard/Core?
从.Net core 2.2迁移到.Net Core 3.0