zoukankan      html  css  js  c++  java
  • ASP NET Core ---POST, PUT, PATCH, DELETE,Model 验证

    参照 草根专栏- ASP.NET Core + Ng6 实战:https://v.qq.com/x/page/u0765jbwc6f.html

    一、POST

    安全性和幂等性

    1. 安全性是指方法执行后并不会改变资源的表述
    2. 幂等性是指方法无论执行多少次都会得到同样的结果

    POST添加资源:

    • 不安全, 不幂等
    • 参数 [FromBody]
    • 返回 201 Create :    CreatedAtRoute(): 它允许响应里带着Location Header,在这个Location Header里包含着一个uri,通过这个uri就可以GET到我们刚刚创建好的资源
    • HATEOAS

     (1)添加 PostAddResource.cs 类

    namespace BlogDemo.Infrastructure.Resources
    {
        public class PostAddResource  
        {
            public string Title { get; set; }
            public string Body { get; set; }
    
            public string Remark { get; set; }
        }
    }

    (2)添加映射

    namespace BlogDemo.Api.Extensions
    {
        public class MappingProfile:Profile
        {
            public MappingProfile()
            {
                CreateMap<Post, PostDTO>().ForMember(dest=>dest.Updatetime,opt=>opt.MapFrom(src=>src.LastModified));
                CreateMap<PostDTO, Post>();
                CreateMap<PostAddResource, Post>();
            }
        }
    }

    (3)添加post方法:

            [HttpPost(Name = "CreatePost")]
             public async Task<IActionResult> Post([FromBody] PostAddResource postAddResource)
            {
                if (postAddResource == null)
                {
                    return BadRequest();
                }
    
             
    
                var newPost = _mapper.Map<PostAddResource, Post>(postAddResource);
    
                newPost.Author = "admin";
                newPost.LastModified = DateTime.Now;
    
                _postRepository.AddPost(newPost);
    
                if (!await _unitOfWork.SaveAsync())
                {
                    throw new Exception("Save Failed!");
                }
    
                var resultResource = _mapper.Map<Post, PostDTO>(newPost);
    
                var links = CreateLinksForPost(newPost.Id);
                var linkedPostResource = resultResource.ToDynamic() as IDictionary<string, object>;
                linkedPostResource.Add("links", links);
    
                return CreatedAtRoute("GetPost", new { id = linkedPostResource["Id"] }, linkedPostResource);
            }
    View Code

      (4) 测试:

     二、Model验证:

              定义验证规则
              检查验证规则
              把验证错误信息发送给API的消费者

    1、验证方式:

              内置验证:
                   DataAnnotation
                   ValidationAttribute
                  IValidatebleObject
              第三方: FluentValidation

    2、使用FluentValidation组件(关注点分离)

            (1) 安装:                  

                      FluentValidation.AspNetCore
                      FluentValidation

             (2) 为每一个Resource建立验证器:

                       继承AbstractValidator<T>

    namespace BlogDemo.Infrastructure.Resources
    {
       public class PostAddResourceValidator:AbstractValidator<PostAddResource>
        {
            public PostAddResourceValidator()
            {
                RuleFor(x => x.Title).NotEmpty()
                    .WithName("标题").WithMessage("{PropertyName}是必须填写的")
                    .MaximumLength(50).WithMessage("{PropertyName}的最大长度是{MaxLength}");
    
                RuleFor(x => x.Body).NotEmpty()
                    .WithName("正文").WithMessage("{PropertyName}是必须填写的")
                    .MinimumLength(50).WithMessage("{PropertyName}的最大长度是{MinLength}");
            }
        }
    }

                (3)配置:

    services.AddMvc(……).AddFluentValidation();
    services.AddTransient<IValidator<PostAddResource>, PostAddResourceValidator>();

                 (4)Action添加验证:

                         ModelState.IsValid
                         ModelState
                               它是一个字典,包含了Model的状态以及Model所绑定的验证
                               对于提交的每个属性,它都包含了一个错误信息的集合
                               返回: 422 Unprocessable Entity
                          验证错误信息在响应的body里面带回去

                if (!ModelState.IsValid)
                {
                    return  UnprocessableEntity(ModelState);
                }

                   (5)测试

                (6)Action添加Accpet和Content-Type 的自定义hateoas

            [RequestHeaderMatchingMediaType("Content-Type", new[] { "application/vnd.cgzl.post.create+json" })]
            [RequestHeaderMatchingMediaType("Accept", new[] { "application/vnd.cgzl.hateoas+json" })]

                  (7)  staupDevelopment 注册hateoas

                services.AddMvc(option => {
                    option.ReturnHttpNotAcceptable = true;
                  //  option.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
                    var outputFormatter = option.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault();
                    if (outputFormatter != null)
                    {
                        outputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.hateoas+json");
                    }
    
                    var intputFormatter = option.InputFormatters.OfType<JsonInputFormatter>().FirstOrDefault();
                    if (intputFormatter != null)
                    {
                        intputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.post.create+json");
                        intputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.post.update+json");
                    }
    
                })

                  (8)测试:

    3、自定义错误验证返回结果:

          (1) 添加MyUnprocessableEntityObjectResult.cs   ResourceValidationError.cs   ResourceValidationResult.cs 类

    namespace BlogDemo.Api.Helpers
    {
        public class MyUnprocessableEntityObjectResult : UnprocessableEntityObjectResult
        {
            public MyUnprocessableEntityObjectResult(ModelStateDictionary modelState) : base(new ResourceValidationResult(modelState))
            {
                if (modelState == null)
                {
                    throw new ArgumentNullException(nameof(modelState));
                }
                StatusCode = 422;
            }
        }
    }
    View Code
    namespace BlogDemo.Api.Helpers
    {
        public class ResourceValidationError
        {
            public string ValidatorKey { get; private set; }
            public string Message { get; private set; }
    
            public ResourceValidationError(string message, string validatorKey = "")
            {
                ValidatorKey = validatorKey;
                Message = message;
            }
        }
    }
    View Code
    namespace BlogDemo.Api.Helpers
    {
        public class ResourceValidationResult : Dictionary<string, IEnumerable<ResourceValidationError>>
        {
            public ResourceValidationResult() : base(StringComparer.OrdinalIgnoreCase)
            {
    
            }
    
            public ResourceValidationResult(ModelStateDictionary modelState)
                : this()
            {
                if (modelState == null)
                {
                    throw new ArgumentNullException(nameof(modelState));
                }
    
                foreach (var keyModelStatePair in modelState)
                {
                    var key = keyModelStatePair.Key;
                    var errors = keyModelStatePair.Value.Errors;
                    if (errors != null && errors.Count > 0)
                    {
                        var errorsToAdd = new List<ResourceValidationError>();
                        foreach (var error in errors)
                        {
                            var keyAndMessage = error.ErrorMessage.Split('|');
    
                            if (keyAndMessage.Length > 1)
                            {
                                errorsToAdd.Add(new ResourceValidationError(keyAndMessage[1], keyAndMessage[0]));
                            }
                            else
                            {
                                errorsToAdd.Add(new ResourceValidationError(keyAndMessage[0]));
                            }
                        }
                        Add(key, errorsToAdd);
                    }
                }
            }
        }
    }
    View Code

           (2)Action中添加自定义验证:

            public async Task<IActionResult> Post([FromBody] PostAddResource postAddResource)
            {
          
                if (!ModelState.IsValid)
                {
                    return new  MyUnprocessableEntityObjectResult(ModelState);
                }
             }

                (3)测试

    三、Delete

    1、 在PostRepository 添加Delete方法

            public void Delete(Post post)
            {
                _myContext.Posts.Remove(post);
            }

     2、Action添加Delete方法:

            [HttpDelete("{id}", Name = "DeletePost")]
            public async Task<IActionResult> DeletePost(int id)
            {
                var post = await _postRepository.GetPostId(id);
                if (post == null)
                {
                    return NotFound();
                }
    
                _postRepository.Delete(post);
    
                if (!await _unitOfWork.SaveAsync())
                {
                    throw new Exception($"Deleting post {id} failed when saving.");
                }
    
                return NoContent();
            }

    四、PUT(整体更新)

    1、添加PostUpdateResource.cs 类;

        public class PostUpdateResource : PostAddOrUpdateResource
        {
            
        }

    2、重构  PostAddOrUpdateResourceValidator.cs ,改成泛型

    namespace BlogDemo.Infrastructure.Resources
    {
       public class PostAddOrUpdateResourceValidator<T> : AbstractValidator<T> where T: PostAddOrUpdateResource
        {
            public PostAddOrUpdateResourceValidator()
            {
                RuleFor(x => x.Title).NotEmpty()
                    .WithName("标题").WithMessage("required|{PropertyName}是必须填写的")
                    .MaximumLength(50).WithMessage("maxLength|{PropertyName}的最大长度是{MaxLength}");
    
                RuleFor(x => x.Body).NotEmpty()
                    .WithName("正文").WithMessage("required|{PropertyName}是必须填写的")
                    .MinimumLength(10).WithMessage("minLength|{PropertyName}的最小长度是{MinLength}");
            }
        }
    }

    3、资源验证注册:

                services.AddTransient<IValidator<PostUpdateResource>, PostAddOrUpdateResourceValidator<PostUpdateResource>>();

    4、添加Map

    namespace BlogDemo.Api.Extensions
    {
        public class MappingProfile:Profile
        {
            public MappingProfile()
            {
                CreateMap<Post, PostDTO>().ForMember(dest=>dest.Updatetime,opt=>opt.MapFrom(src=>src.LastModified));
                CreateMap<PostDTO, Post>();
                CreateMap<PostAddResource, Post>();
                CreateMap<PostUpdateResource, Post>();
            }
        }
    }

    5、添加自定义hateoas

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc(option => {
                    option.ReturnHttpNotAcceptable = true;
                  //  option.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
                    var outputFormatter = option.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault();
                    if (outputFormatter != null)
                    {
                        outputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.hateoas+json");
                    }
    
                    var intputFormatter = option.InputFormatters.OfType<JsonInputFormatter>().FirstOrDefault();
                    if (intputFormatter != null)
                    {
                        intputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.post.create+json");
                        intputFormatter.SupportedMediaTypes.Add("application/vnd.cgzl.post.update+json");
                    }
    
                })
    }

    6、Action添加PUT方法:

            [HttpPut("{id}", Name = "UpdatePost")]
            [RequestHeaderMatchingMediaType("Content-Type", new[] { "application/vnd.cgzl.post.update+json" })]
            public async Task<IActionResult> UpdatePost(int id, [FromBody] PostUpdateResource postUpdate)
            {
                if (postUpdate == null)
                {
                    return BadRequest();
                }
    
                if (!ModelState.IsValid)
                {
                    return new MyUnprocessableEntityObjectResult(ModelState);
                }
    
                var post = await _postRepository.GetPostId(id);
                if (post == null)
                {
                    return NotFound();
                }
    
                post.LastModified = DateTime.Now;
                _mapper.Map(postUpdate, post);
    
                if (!await _unitOfWork.SaveAsync())
                {
                    throw new Exception($"Updating post {id} failed when saving.");
                }
                return NoContent();
            }
    View Code

    7、测试

    五、Patch(局部更新)

     

     

    1、 PostRepository 添加Update方法:

            public void Update(Post post)
            {
                _myContext.Entry(post).State = EntityState.Modified;
            }

    2、Action添加Patch方法:

            [HttpPatch("{id}", Name = "PartiallyUpdatePost")]
            public async Task<IActionResult> PartiallyUpdateCityForCountry(int id,
                [FromBody] JsonPatchDocument<PostUpdateResource> patchDoc)
            {
                if (patchDoc == null)
                {
                    return BadRequest();
                }
    
                var post = await _postRepository.GetPostByIdAsync(id);
                if (post == null)
                {
                    return NotFound();
                }
    
                var postToPatch = _mapper.Map<PostUpdateResource>(post);
    
                patchDoc.ApplyTo(postToPatch, ModelState);
    
                TryValidateModel(postToPatch);
    
                if (!ModelState.IsValid)
                {
                    return new MyUnprocessableEntityObjectResult(ModelState);
                }
    
                _mapper.Map(postToPatch, post);
                post.LastModified = DateTime.Now;
                _postRepository.Update(post);
    
                if (!await _unitOfWork.SaveAsync())
                {
                    throw new Exception($"Patching city {id} failed when saving.");
                }
    
                return NoContent();
            }
    View Code

    3、测试

     

    六、HTTP常用方法总结

      

  • 相关阅读:
    HDU 3572 Task Schedule(拆点+最大流dinic)
    POJ 1236 Network of Schools(Tarjan缩点)
    HDU 3605 Escape(状压+最大流)
    HDU 1166 敌兵布阵(分块)
    Leetcode 223 Rectangle Area
    Leetcode 219 Contains Duplicate II STL
    Leetcode 36 Valid Sudoku
    Leetcode 88 Merge Sorted Array STL
    Leetcode 160 Intersection of Two Linked Lists 单向链表
    Leetcode 111 Minimum Depth of Binary Tree 二叉树
  • 原文地址:https://www.cnblogs.com/fuyouchen/p/9596956.html
Copyright © 2011-2022 走看看