网络核心:以动态可变方式将DTO包裹在响应模式中 [英] Net Core: Wrap DTO in Response Pattern in Dynamic Variable Way

查看:145
本文介绍了网络核心:以动态可变方式将DTO包裹在响应模式中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

软件要求要求所有DTO都包含自己的响应类。因此,开发人员基本上将产品DTO包装在基本响应类中。我们有完整的班级列表,每个产品,销售,客户等需要数百个班级,每个班级都在做同样的事情,如下所示。客户端不希望包装为 BaseResponse< Product>或BaseResponse< IEnumerable< ProductDto> ,因为它是嵌套的/不可读的。

Software requirements is asking all DTOs contain their own Response Class. So developers basically wrap Product DTO in Base Response class. We have whole list of class areas, requiring hundreds of classes for Product, Sales, Customer, etc all doing same thing, per below. Client does not want to wrap as BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto> since it is nested/unreadable.

是否存在包装/创建变量类和可读性的方法,无需手动写100个类(可能是扩展方法,动态类,变量,不确定,对任何方法都开放)?

Is there method to wrap/create variable classes and readable, without manually writing 100s of class (maybe extension method, dynamic class, variable, not sure, open to any method)?

注意:响应类可以不同,所以想给出程序员选择创建自动标准类,或创建自己的自定义手动类,因此可以存在两个选择。

Note: Response classes can be different, so want to give programmer option to create automated standard class, or create own customized manual class, so two options can exist.

当前代码:

产品DTO类别:

public class ProductDto
{
    public int ProductId { get; set;},
    public string ProductName { get; set;},
    public string ProductDescription { get; set;},
    public float SalesAmount { get; set;}
}

BaseResponse:

public class BaseResponse<T>
{
    [Required, ValidateObject]
    public T Body { get; set; }
    public bool HasError { get; set; }
    public string Error { get; set; }
}

个人回复:

public class GetAllProductResponse : BaseResponse<IEnumerable<ProductDto>>
{
}

public class GetProductResponse : BaseResponse<ProductDto>
{
}

public class UpdateProductResponse : BaseResponse<ProductDto>
{
}

建议的代码:

public static class ResponseExtensions
{
    public static BaseRequestResponse<T> GetAllResponse<T> (this T obj) where T:class
    {
        return BaseRequestResponse<IEnumerable<T>>;
    }

    public static BaseRequestResponse<T> GetResponse<T>(this T obj) where T : class
    {
        return BaseRequestResponse<T>;
    }

    public static BaseRequestResponse<T> UpdateResponse<T>(this T obj) where T : class
    {
        return BaseRequestResponse<T>;
    }
}

所以代码现在看起来像这样,

So the code will now look as this,

ProductDTO.GetAllResponse
ProductDTO.GetResponse
ProductDTO.UpdateResponse

这是一种好方法,具有体系结构性,还是应该应用其他方法?
这可能行不通,因为任何中间层发送/接收响应都需要称为BaseResponse< IEnumerable< ProductDto>等。

Is this a good method, architecturally sound, or should something else be applied? This probably will not work, since any middle tier layer sending/receiving response will need refer to as BaseResponse< IEnumerable< ProductDto >, etc.

顺便说一句,如果走这条路线,在这里接收到编译错误

By the way,if going this route, receiving compilation error here

'BaseRequestResponse<T>' is a type, which is not valid in the given context 


$ b $中无效b

更新:
这就是我们使用DTO和响应的方式

Update: This is how we use DTO and Response

public async Task<ActionResult<GetProductResponse>> GetByProduct(int id)
{
    try
    {
        var productdto = await productAppService.GetProductById(id);
        var response = new GetProductResponse { Body = productdto };
        return Ok(response);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, ex.Message);
        var response = new GetDocumentResponse { HasError = true, Error = ex.Message };
        return StatusCode(StatusCodes.Status500InternalServerError, response);
    }
}


推荐答案

除非您实际上需要控制器来提及那些特定类型,更灵活的解决方案是使用结果过滤器。结果过滤器在控制器操作之后运行,并允许您转换和替换原先产生的结果。

Unless you actually need your controllers to mention those specific types, a more flexible solution would be to use result filters. Result filters run after the controller action and allow you to transform and replace the result that waas produced.

我已经在相关答案

然后您可以应用该过滤器,例如使用 [TypeFilter] 属性:

You can then apply that filter, e.g. using a [TypeFilter] attribute:

[TypeFilter(typeof(ApiResultFilter))]
public ActionResult<ProductDto> GetProduct(int id)
{
    //
    return product;
}

这也可以作为控制器的属性,将其应用于所有操作

This also works as an attribute on the controller, to apply it to all actions of the decorated controller.

或通过在启动中对其进行配置将其全局应用于所有操作结果:

Or apply it globally for all action results by configuring it in your Startup:

services.AddMvc(options =>
{
    options.Filters.Add(new ApiResultFilter());
});






关于您的错误,您编写的扩展名确实可以返回SomeType 。但是实际上您必须创建 BaseResponse< T> 类型并将结果对象放入其中,例如:


Regarding your error, the extensions you wrote just do return SomeType. But you will actually have to create your BaseResponse<T> type and place the result object into it, e.g.:

public static BaseRequestResponse<T> GetResponse<T>(this T obj) where T : class
{
    return new BaseRequestResponse<T>()
    {
        Body = obj,
    };
}






关于您的您发布的 DTO和响应示例,这正是您要使用结果过滤器的示例。这个想法是使控制器动作尽可能具体。任何样板物料应提取到可重复使用的过滤器中。因此最终的操作可能看起来像这样:


In regards to your "DTO and response" example you posted, that’s exactly what you would use result filters for. The idea is to make controller actions as concrete as possible. Any boilerplate stuff should be extracted into filters that can be reused. So the final action may look like this:

public async Task<ActionResult<Product>> GetByProduct(int id)
{
    return await productAppService.GetProductById(id);
}

然后您将使用结果过滤器,以包装类型包装该产品,并使用异常过滤器处理异常以返回自定义错误响应对象。

You would then use a result filter to wrap that product with your wrapper type, and use exception filters to handle exceptions to return a custom error response object instead.

这篇关于网络核心:以动态可变方式将DTO包裹在响应模式中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆