gpt4 book ai didi

c# - ASP.NET Core处理Web API中的自定义响应/输出格式的方法

转载 作者:行者123 更新时间:2023-12-02 01:16:58 25 4
gpt4 key购买 nike

我想创建自定义JSON格式,该格式将响应包装在数据中,并返回Content-Type,例如


vnd.myapi + json


目前,我已经创建了包装类,并在控制器中返回了包装类,但是如果可以在后台进行处理,那就更好了:

public class ApiResult<TValue>
{
[JsonProperty("data")]
public TValue Value { get; set; }

[JsonExtensionData]
public Dictionary<string, object> Metadata { get; } = new Dictionary<string, object>();

public ApiResult(TValue value)
{
Value = value;
}
}

[HttpGet("{id}")]
public async Task<ActionResult<ApiResult<Bike>>> GetByIdAsync(int id)
{
var bike = _dbContext.Bikes.AsNoTracking().SingleOrDefault(e => e.Id == id);
if (bike == null)
{
return NotFound();
}
return new ApiResult(bike);
}

public static class ApiResultExtensions
{
public static ApiResult<T> AddMetadata<T>(this ApiResult<T> result, string key, object value)
{
result.Metadata[key] = value;
return result;
}
}


我想返回以下响应:

{
"data": { ... },
"pagination": { ... },
"someothermetadata": { ... }
}


但是在我的控制器操作中,必须以某种方式将分页添加到元数据中,当然,这里有一些有关内容协商的文章: https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-2.1但我仍然想确定自己处在正确的轨道上。

如果使用我的自定义格式化程序在后台进行处理,那么我将如何向其中添加诸如分页的元数据,而不是在“数据”中而不是在其中?

当拥有一个自定义格式化程序时,我仍然希望有某种方式可以从我的控制器或通过某种机制向其添加元数据,以便该格式可以扩展。

上面方法的优点或缺点是,它与所有序列化器xml,json,yaml等一起使用。通过具有自定义格式化程序,它可能仅适用于json,并且我将需要创建一些不同的格式化程序来支持我所使用的所有格式想。

最佳答案

好的,在花费大量时间使用ASP.NET Core之后,基本上可以想到4种方法来解决此问题。老实说,该主题本身非常复杂且涉及面广,我认为这没有灵丹妙药或最佳实践。
对于自定义Content-Type(假设您要实现application/hal+json),正式的方法(也许也是最优雅的方法)是创建custom output formatter。这样,您的操作对输出格式一无所知,但由于依赖项注入机制和作用域生存期的存在,您仍然可以控制控制器内部的格式化行为。

1. Custom output formatters
这是OData official C# librariesjson:api framework for ASP.Net Core使用的最流行的方式。可能是实现超媒体格式的最佳方法。
要从控制器控制您的自定义输出格式化程序,您必须创建自己的“上下文”以在控制器和自定义格式化程序之间传递数据,并将其添加到具有作用域生存期的DI容器中:
services.AddScoped<ApiContext>();
这样,每个请求将只有一个ApiContext实例。您可以将其注入到控制器和输出格式化程序中,并在它们之间传递数据。
您也可以使用ActionContextAccessorHttpContextAccessor并在自定义输出格式化程序中访问控制器和操作。要访问控制器,必须将ActionContextAccessor.ActionContext.ActionDescriptor强制转换为ControllerActionDescriptor。然后,您可以使用IUrlHelper和操作名称在输出格式化程序内部生成链接,以便控制器摆脱这种逻辑。
IActionContextAccessor是可选的,默认情况下不会添加到容器中,要在项目中使用它,必须将其添加到IoC容器中。
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>()
在自定义输出格式化程序中使用服务:

您不能在formatter类中进行构造函数依赖项注入。例如,您无法通过将logger参数添加到构造函数来获取logger。要访问服务,您必须使用传递到您的方法中的上下文对象。

https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-2.0#read-write
wash草支持:
Swashbuckle显然不会使用这种方法和带过滤器的方法生成正确的响应示例。您可能必须创建自定义document filter
示例:如何添加分页链接:
通常,分页,过滤是使用specification pattern解决的,您通常会在[Get]操作中使用一些通用的规范模型。然后,您可以在格式化程序中确定当前执行的操作是否按其参数类型或其他方式返回元素列表:

var specificationParameter = actionContextAccessor.ActionContext.ActionDescriptor.Parameters.SingleOrDefault(p => p.ParameterType == typeof(ISpecification<>));
if (specificationParameter != null)
{
// add pagination links or whatever
var urlHelper = new UrlHelper(actionContextAccessor.ActionContext);
var link = urlHelper.Action(new UrlActionContext()
{
Protocol = httpContext.Request.Scheme,
Host = httpContext.Request.Host.ToUriComponent(),
Values = yourspecification
})
}

优势(或没有):

您的操作未定义格式,它们对格式或如何生成链接以及将其放置在位置一无所知。他们只知道结果类型,而不知道描述结果的元数据。

可重复使用,您可以轻松地将格式添加到其他项目中,而不必担心如何在操作中处理它。与链接,格式相关的所有内容均在后台进行处理。您的动作无需任何逻辑。

序列化实现由您决定,您不必使用Newtonsoft.JSON,例如,可以使用 Jil


缺点:

这种方法的一个缺点是它仅适用于特定的Content-Type。因此,为了支持XML,我们需要创建另一个具有Content-Type的自定义输出格式器,例如 vnd.myapi+xml而不是 vnd.myapi+json

我们不直接处理动作结果

实施起来可能更复杂


2. Result filters
结果过滤器使我们能够定义某种行为,这些行为将在我们的操作返回之前执行。我认为这是某种形式的挂机。我认为这不是包装我们的回复的正确位置。
它们可以按操作或全局应用于所有操作。
就个人而言,我不会将其用于此类事情,而是将其用作第三种选择的补充。
样本结果过滤器包装输出:
public class ResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objectResult)
{
objectResult.Value = new ApiResult { Data = objectResult.Value };
}
}

public void OnResultExecuted(ResultExecutedContext context)
{
}
}

您可以在 IActionFilter中放入相同的逻辑,它也应该工作:
public class ActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
}

public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is ObjectResult objectResult)
{
objectResult.Value = new ApiResult { Data = objectResult.Value };
}
}
}

这是包装您的响应的最简单方法,特别是如果您已经拥有带有控制器的现有项目。因此,如果您关心时间,请选择此时间。
3.在操作中明确格式化/包装结果
(我在问题中的处理方式)
在这里也可以使用它: https://github.com/nbarbettini/BeautifulRestApi/tree/master/src亲自实现 https://github.com/ionwg/ion-doc/blob/master/index.adoc我认为这将更适合于自定义输出格式化程序。
这可能是最简单的方法,但同时也将您的API“密封”为该特定格式。这种方法有很多优点,但也可能有一些缺点。例如,如果您想更改API的格式,那么就很难做到这一点,因为您的操作与该特定的响应模型结合在一起,并且如果您的操作中对该模型有某种逻辑,例如,重新为下一个和上一个添加分页链接。实际上,您必须重写所有操作和格式化逻辑以支持该新格式。使用自定义输出格式化程序,甚至可以根据Content-Type标头支持两种格式。
优点:

适用于所有Content-Type,格式是API不可或缺的一部分。
Swashbuckle开箱即用,在使用 ActionResult<T>(2.1+)时,还可以将 [ProducesResponseType]属性添加到操作中。

缺点:

您不能使用 Content-Type标头控制格式。 application/jsonapplication/xml始终保持相同。 (也许是优势?)
您的操作负责返回正确格式的响应。 return new ApiResponse(obj);之类的东西,或者您可以创建扩展方法并像 obj.ToResponse()这样调用它,但是您始终必须考虑正确的响应格式。
从理论上讲,像 vnd.myapi+json这样的自定义Content-Type不会带来任何好处,仅仅为该名称实现自定义输出格式化程序就没有意义,因为格式化仍然是控制器操作的责任。

我认为这更像是正确处理输出格式的快捷方式。我认为在 single responsibility principle之后,它应该是输出格式化程序的工作,因为名称表明它格式化了输出。
4. Custom middleware
您可以做的最后一件事是自定义中间件,您可以从那里解析 IActionResultExecutor并像在MVC控制器中一样返回 IActionResult
https://github.com/aspnet/Mvc/issues/7238#issuecomment-357391426
您还可以解析 IActionContextAccessor以访问MVC的操作上下文,如果需要访问控制器信息,可以将 ActionDescriptor强制转换为 ControllerActionDescriptor
文件说:

资源过滤器就像中间件一样工作,因为它们围绕管道中稍后出现的所有内容的执行。但是过滤器与中间件的不同之处在于它们是MVC的一部分,这意味着它们可以访问MVC上下文和构造。

但这并不是完全正确的,因为您可以访问操作上下文,并且可以从中间件返回操作结果,该结果是MVC的一部分。

如果您要添加任何内容,请分享自己的经验和优点或缺点,请随时发表评论。

关于c# - ASP.NET Core处理Web API中的自定义响应/输出格式的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50406111/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com