鼎鼎知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5、Result模式,一站式解决响应问题.md 7.3KB

3 年之前
3 年之前
3 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # 引出`Result`模式
  2. 通常的方式
  3. ```
  4. public Customer GetCustomer(int customerId)
  5. {
  6. // more logic
  7. return customer;
  8. }
  9. public Customer CreateCustomer(string firstName, string lastName)
  10. {
  11. // more logic
  12. return customer;
  13. }
  14. ```
  15. 在逻辑中,我们可能会判断`customerId`不存在,判断`lastName`没有提供,甚至当前用户是否有权限创建用户。
  16. 我们在处理正常和异常这两种情况。
  17. `Result`模式用来对这两种情况进行封装,并且统一了返回结果的格式。
  18. ```
  19. public async Task<Result<BlogCategory>> UpdateAsync(BlogCategory blogCategory)
  20. {
  21. if (Guid.Empty == blogCategory.BlogCategoryId) return Result<BlogCategory>.NotFound();//没有找到
  22. var validator = new BlogCategoryValidator();
  23. var validation = await validator.ValidateAsync(blogCategory);
  24. if (!validation.IsValid)
  25. {
  26. return Result<BlogCategory>.Invalid(validation.AsErrors());//验证异常
  27. }
  28. var itemToUpdate = (await GetByIdAsync(blogCategory.BlogCategoryId)).Value;
  29. if (itemToUpdate == null)
  30. {
  31. return Result<BlogCategory>.NotFound();
  32. }
  33. itemToUpdate.Update(blogCategory.Name, blogCategory.ParentId);
  34. return Result<BlogCategory>.Success(await _blogCategoryRepository.UpdateAsync(itemToUpdate));
  35. }
  36. ```
  37. 结果
  38. ![ddd12](F:\SourceCodes\DDWiki\专题\后端\DDD\ddd12.png)
  39. # 接口
  40. ```
  41. [ApiController]
  42. [Route("mediatr/[controller]")]
  43. public class WeatherForecastController : ControllerBase
  44. {
  45. private readonly IMediator _mediator;
  46. private readonly ILogger<WeatherForecastController> _logger;
  47. public WeatherForecastController(
  48. IMediator mediator,
  49. ILogger<WeatherForecastController> logger)
  50. {
  51. _mediator = mediator;
  52. _logger = logger;
  53. }
  54. /// <summary>
  55. /// This uses a filter to convert an Ardalis.Result return type to an ActionResult.
  56. /// This filter could be used per controller or globally!
  57. /// </summary>
  58. /// <param name="model"></param>
  59. /// <returns></returns>
  60. [TranslateResultToActionResult]//把Result的结果转换成ActionResult的结果,可以定义我们自己的特性,这点特别重要
  61. [HttpPost("Create")]
  62. public Task<Result<IEnumerable<WeatherForecast>>> CreateForecast([FromBody] NewForecastCommand model)
  63. {
  64. // One might try to perform translation from Result<T> to an appropriate IActionResult from within a MediatR pipeline
  65. // Unfortunately without having Result<T> depend on IActionResult there doesn't appear to be a way to do this, so this
  66. // example is still using the TranslateResultToActionResult filter.
  67. return _mediator.Send(model);
  68. }
  69. public class NewForecastCommand : IRequest<Result<IEnumerable<WeatherForecast>>>
  70. {
  71. [Required]
  72. public string PostalCode { get; set; }
  73. }
  74. public class NewForecastHandler : IRequestHandler<NewForecastCommand, Result<IEnumerable<WeatherForecast>>>
  75. {
  76. private readonly WeatherService _weatherService;
  77. public NewForecastHandler(WeatherService weatherService)
  78. {
  79. _weatherService = weatherService;
  80. }
  81. public Task<Result<IEnumerable<WeatherForecast>>> Handle(NewForecastCommand request, CancellationToken cancellationToken)
  82. {
  83. var result = _weatherService.GetForecastAsync(new ForecastRequestDto { PostalCode = request.PostalCode });
  84. return result;
  85. }
  86. }
  87. }
  88. ```
  89. 在`WeatherService`中
  90. ```
  91. public class WeatherService
  92. {
  93. public WeatherService(IStringLocalizer<WeatherService> stringLocalizer)
  94. {
  95. _stringLocalizer = stringLocalizer;
  96. }
  97. private static readonly string[] Summaries = new[]
  98. {
  99. "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
  100. };
  101. private IStringLocalizer<WeatherService> _stringLocalizer;
  102. public Task<Result<IEnumerable<WeatherForecast>>> GetForecastAsync(ForecastRequestDto model)
  103. {
  104. return Task.FromResult(GetForecast(model));
  105. }
  106. public Result<IEnumerable<WeatherForecast>> GetForecast(ForecastRequestDto model)
  107. {
  108. if (model.PostalCode == "NotFound") return Result<IEnumerable<WeatherForecast>>.NotFound();//没有找到
  109. // validate model
  110. if (model.PostalCode.Length > 10)
  111. {
  112. return Result<IEnumerable<WeatherForecast>>.Invalid(new List<ValidationError> {
  113. new ValidationError
  114. {
  115. Identifier = nameof(model.PostalCode),
  116. ErrorMessage = _stringLocalizer["PostalCode cannot exceed 10 characters."].Value }
  117. });//模型验证失败
  118. }
  119. // test value
  120. if (model.PostalCode == "55555")
  121. {
  122. return new Result<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 1)//返回成功的结果
  123. .Select(index =>
  124. new WeatherForecast
  125. {
  126. Date = DateTime.Now,
  127. TemperatureC = 0,
  128. Summary = Summaries[0]
  129. }));
  130. }
  131. var rng = new Random();
  132. return new Result<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 5)//返回成功的结果
  133. .Select(index => new WeatherForecast
  134. {
  135. Date = DateTime.Now.AddDays(index),
  136. TemperatureC = rng.Next(-20, 55),
  137. Summary = Summaries[rng.Next(Summaries.Length)]
  138. })
  139. .ToArray());
  140. }
  141. }
  142. ```
  143. 如何影射`Result`结果呢?
  144. ```
  145. public class ultAttribute : ActionFilterAttribute
  146. {
  147. public override void OnActionExecuted(ActionExecutedContext context)
  148. {
  149. if (!((context.Result as ObjectResult)?.Value is IResult result)) return;
  150. if (!(context.Controller is ControllerBase controller)) return;
  151. //判断IResult的ResultStatus
  152. if (result.Status == ResultStatus.NotFound)
  153. context.Result = controller.NotFound();
  154. if (result.Status == ResultStatus.Invalid)
  155. {
  156. foreach (var error in result.ValidationErrors)
  157. {
  158. // TODO: Fix after updating to 3.0.0
  159. (context.Controller as ControllerBase)?.ModelState.AddModelError(error.Identifier, error.ErrorMessage);
  160. }
  161. context.Result = controller.BadRequest(controller.ModelState);
  162. }
  163. if (result.Status == ResultStatus.Ok)
  164. {
  165. context.Result = new OkObjectResult(result.GetValue());
  166. }
  167. }
  168. }
  169. ```
  170. `IResult`
  171. ```
  172. public interface IResult
  173. {
  174. ResultStatus Status { get; }
  175. IEnumerable<string> Errors { get; }
  176. List<ValidationError> ValidationErrors { get; }
  177. Type ValueType { get; }
  178. Object GetValue();
  179. }
  180. ```
  181. `ResultStatus`
  182. ```
  183. public enum ResultStatus
  184. {
  185. Ok,
  186. Error,
  187. Forbidden,
  188. Invalid,
  189. NotFound
  190. }
  191. ```