refacto site settings
Some checks failed
Dotnet build and test / build (pull_request) Failing after 8m29s
Some checks failed
Dotnet build and test / build (pull_request) Failing after 8m29s
This commit is contained in:
parent
3e7e76edf2
commit
3bc4142383
33 changed files with 399 additions and 315 deletions
|
|
@ -57,12 +57,31 @@ namespace Yavsc.Blogs.Controllers
|
|||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutBlog(long id)
|
||||
{
|
||||
var blog = await ReadPutBlogRequestAsync();
|
||||
var blog = await ReadBlogRequestAsync();
|
||||
if (blog is null)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
// Without [FromBody], the data-annotations attributes
|
||||
// ([Required] on Title, etc.) are no longer evaluated by
|
||||
// the model binder — the body is read manually. Run
|
||||
// validation explicitly so an empty Title still yields
|
||||
// 400 (and not a 500 from the DB NOT NULL constraint).
|
||||
var validationContext = new System.ComponentModel.DataAnnotations.ValidationContext(blog);
|
||||
var validationResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
|
||||
if (!System.ComponentModel.DataAnnotations.Validator.TryValidateObject(
|
||||
blog, validationContext, validationResults, validateAllProperties: true))
|
||||
{
|
||||
foreach (var result in validationResults)
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
result.MemberNames.FirstOrDefault() ?? string.Empty,
|
||||
result.ErrorMessage ?? "Invalid value.");
|
||||
}
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
// These properties are server-managed or optional graph members and
|
||||
// should not block JSON payloads coming from API clients.
|
||||
ModelState.Remove(nameof(Models.Blog.BlogPost.Author));
|
||||
|
|
@ -93,7 +112,8 @@ namespace Yavsc.Blogs.Controllers
|
|||
|
||||
try
|
||||
{
|
||||
await blogSpotService.Modify(User, blog, files);
|
||||
await blogSpotService.Modify(User, blog);
|
||||
blogSpotService.AttachFiles(files, User.GetUserId(), id);
|
||||
}
|
||||
catch (AuthorizationFailureException)
|
||||
{
|
||||
|
|
@ -105,8 +125,14 @@ namespace Yavsc.Blogs.Controllers
|
|||
|
||||
// POST: api/v1/blogspot
|
||||
[HttpPost]
|
||||
public IActionResult PostBlog([FromBody] Models.Blog.BlogPost blog)
|
||||
public async Task<IActionResult> PostBlog()
|
||||
{
|
||||
var blog = await ReadBlogRequestAsync();
|
||||
if (blog is null)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
// These properties are server-managed or optional graph members and
|
||||
// should not block JSON payloads coming from API clients.
|
||||
ModelState.Remove(nameof(Models.Blog.BlogPost.Author));
|
||||
|
|
@ -115,35 +141,46 @@ namespace Yavsc.Blogs.Controllers
|
|||
ModelState.Remove(nameof(Models.Blog.BlogPost.UserCreated));
|
||||
ModelState.Remove(nameof(Models.Blog.BlogPost.UserModified));
|
||||
|
||||
// Without [FromBody], the data-annotations attributes
|
||||
// ([Required] on Title, etc.) are no longer evaluated by
|
||||
// the model binder — the body is read manually. Run
|
||||
// validation explicitly so an empty Title still yields
|
||||
// 400 (and not a 500 from the DB NOT NULL constraint).
|
||||
var validationContext = new System.ComponentModel.DataAnnotations.ValidationContext(blog);
|
||||
var validationResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
|
||||
if (!System.ComponentModel.DataAnnotations.Validator.TryValidateObject(
|
||||
blog, validationContext, validationResults, validateAllProperties: true))
|
||||
{
|
||||
foreach (var result in validationResults)
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
result.MemberNames.FirstOrDefault() ?? string.Empty,
|
||||
result.ErrorMessage ?? "Invalid value.");
|
||||
}
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
// The BlogSpotService.Create() signature requires an
|
||||
// IFormFileCollection for file uploads. Reading
|
||||
// Request.Form.Files when the request is a plain JSON
|
||||
// body (e.g. from PostIt) throws
|
||||
// "This request does not have a Content-Type header.
|
||||
// Forms are available from requests with bodies like
|
||||
// POSTs and a form Content-Type of either
|
||||
// application/x-www-form-urlencoded or
|
||||
// multipart/form-data."
|
||||
//
|
||||
// Two valid use cases for this endpoint:
|
||||
// 1. JSON body only (no files) — PostIt path.
|
||||
// 2. multipart/form-data with a 'blog' field + 0..N
|
||||
// files — future browser / server-rendered path.
|
||||
// files — PostIt "create with attachments" path.
|
||||
//
|
||||
// Branch on HasFormContentType: pass the form files when
|
||||
// present, pass an empty collection otherwise. The
|
||||
// FileSystem branch in BlogSpotService.Create then
|
||||
// short-circuits to "no files to handle".
|
||||
// present, pass an empty collection otherwise. Reading
|
||||
// Request.Form.Files on a plain JSON request throws
|
||||
// "This request does not have a Content-Type header…",
|
||||
// and AttachFiles short-circuits on an empty collection.
|
||||
var files = Request.HasFormContentType
|
||||
? Request.Form.Files
|
||||
: (IFormFileCollection)new FormFileCollection();
|
||||
var uid = User.GetUserId();
|
||||
var post = blogSpotService.Create(uid, blog, files);
|
||||
var post = blogSpotService.Create(uid, blog);
|
||||
blogSpotService.AttachFiles(files, uid, post.Id);
|
||||
return CreatedAtRoute("GetBlog", new { id = post.Id },
|
||||
post.GetPayload());
|
||||
}
|
||||
|
|
@ -210,7 +247,17 @@ namespace Yavsc.Blogs.Controllers
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private async Task<Models.Blog.BlogPost?> ReadPutBlogRequestAsync()
|
||||
/// <summary>
|
||||
/// Read a <see cref="Models.Blog.BlogPost"/> from either a
|
||||
/// JSON body or a multipart/form-data request carrying the
|
||||
/// payload in a <c>blog</c> form field. Shared by
|
||||
/// <see cref="PostBlog"/> and <see cref="PutBlog"/>: both
|
||||
/// accept the two shapes so PostIt can attach files on
|
||||
/// create as well as on update. Returns <c>null</c> (with
|
||||
/// the reason recorded in <c>ModelState</c>) when the
|
||||
/// multipart form has no usable <c>blog</c> field.
|
||||
/// </summary>
|
||||
private async Task<Models.Blog.BlogPost?> ReadBlogRequestAsync()
|
||||
{
|
||||
if (!Request.HasFormContentType)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue