-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Help needed] Provide a POST example #76
Comments
@RichardsonWTR I'll get back to you soon, was away... |
It is a funny thing, based on another issue you raised I have recently started creating ASP.NET Core 3.1 hypermedia API's, etc. So with that said and your request here, I think it is time I first upgrade this sample to ASP.NET Core 3.1 and add POST, PATCH, and DELETE examples as well. As you discovered in another thread, you need to manually add JSON.NET as the JSON serializer/deserializer as the framework was developed and thus integrated with JSON.NET. As a sidebar, a better approach would be to abstract away the JSON serialization/deserialization so the framework is not directly coupled with any specific JSON serializer, etc. I have done that on other projects but unfortunately this framework is directly integrated with JSON.NET. Not the end of the world though as JSON.NET is pretty good and has served this framework well for a long time... Ok, will start enhancing the blogging tutorial, etc. |
Thank you for your hard work with this library. |
JsonApiFramework does a lot of the heavy lifting when it comes to dealing with JSON:API, what you are trying to do can be done somewhat like this. [HttpPost("articles")]
public IActionResult Post([FromBody] Document inDocument)
{
var displayUrl = _httpContextAccessor.HttpContext.Request.GetDisplayUrl();
var currentRequestUri = new Uri(displayUrl);
using var bloggingDocumentContext = new BloggingDocumentContext(currentRequestUri, inDocument);
var articleResource = bloggingDocumentContext.GetResource<Article>();
var article = new Article
{
ArticleId = RandomData.GetInt(100,1000),
Text = articleResource.Text,
Title = articleResource.Title
};
/////////////////////////////////////////////////////
// Get all relationships for article resouce, then verify the relationship has linkage.
/////////////////////////////////////////////////////
var resourceRelationships = bloggingDocumentContext.GetResourceRelationships<Article>();
var hasRelationshipToAuthor = resourceRelationships.TryGetRelationship("author", out var authorRealtionship);
if (hasRelationshipToAuthor && !authorRealtionship.IsResourceLinkageNullOrEmpty())
{
var authorResourceLinkage = authorRealtionship.GetToOneResourceLinkage();
article.AuthorId = long.Parse(authorResourceLinkage.Id);
}
var hasRelationshipToBlog = resourceRelationships.TryGetRelationship("blogs", out var blogsRealtionship);
if (hasRelationshipToBlog && !blogsRealtionship.IsResourceLinkageNullOrEmpty())
{
var blogResourceLinkage = blogsRealtionship.GetToOneResourceLinkage();
article.BlogId = long.Parse(blogResourceLinkage.Id);
}
BloggingRepository.AddArticle(article);
/////////////////////////////////////////////////////
// Build JSON API document
/////////////////////////////////////////////////////
var document = bloggingDocumentContext
.NewDocument(currentRequestUri)
.SetJsonApiVersion(JsonApiVersion.Version10)
.Links()
.AddUpLink()
.AddSelfLink()
.LinksEnd()
.Resource(article)
.Relationships()
.AddRelationship("blog", new[] { Keywords.Related })
.AddRelationship("author", new[] { Keywords.Related })
.AddRelationship("comments", new[] { Keywords.Related })
.RelationshipsEnd()
.Links()
.AddSelfLink()
.LinksEnd()
.ResourceEnd()
.WriteDocument();
return Created(document.GetResource().SelfLink(), document);
} You can run the .NET Core 3.1 version of this sample by running the unit test project found here @scott-mcdonald Not sure how far you are into updating the samples project, but if I get a chance I can add the post/delete to my fork since I already updated the project, then I can open a PR against your branch. I'll leave you to handle PATCH :) |
@RichardsonWTR @circleupx So I have an intermediate commit that has the POST and DELETE example implementations. I upgraded the ASP.NET Core project to 3.1 and introduced some higher-level things such as using Sqlite in-memory database and server-side validation of incoming requests, etc. This is not quite production quality code but a more substantial example of the CUD aspect of the framework, etc. The one thing I have not tackled yet is how PATCH is going to work so I have left that as a not implemented for now. |
Any reason why used an exception filter instead of middleware to do error handling? |
And thank you for sharing. Especially the ErrorHandling code. |
@circleupx Personally I was unaware of any middleware for error handling. Hooking into the ASP.NET messages and filter extension points are what I am most familiar with so just following my previous implementation techniques. I will say I have introduced some "middleware" to normalize query parameters so when the server generates hypermedia the order of query parameters is consistent. I did this because I want to use the URL as cache keys so the normalization allows for greater cache key hits, etc. Is the middleware the recommended best practice for error handling? In general, when should we integrate with messages and/or filters versus middleware extension points? I don't have the answers to those questions so I might need to do some research on said topics... Thanks for the feedback ;-) |
I believe a middleware is now the recommended best practice when doing error handling because .NET Core filters are very specific to MVC, where a middleware will catch an error at any point of the .NET Core stack. For example, assume your API exposes a GraphQL endpoint, kinda like this, class Program
{
static async Task Main(string[] args)
{
var app = WebApplication.Create(args);
app.Map("/graphql", async http =>
{
// Implementation details
});
await app.RunAsync();
}
} and a client sends an HTTP request to that /GraphQL endpoint, well if the API generates an error, then it will bubble up to the error handling middleware for you to handle the error, just like how you did it for the exception filter, this is because of the fact that all HTTP request/response pass through all .NET Core middlewares. If you were doing an Exception filter, the exception would never bubble up to the filter because filters are very specific to MVC, and by MVC I mean controllers. At least that has always been my understanding of middlewares vs filters, someone can correct me if I am wrong. So when it comes to global error handling I favor using a middleware, possibly like this, // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception exception)
{
await HandleException(httpContext, exception);
}
}
private Task HandleException(HttpContext httpContext, Exception exception)
{
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var errorsDocument = new ErrorsDocument();
var eventTitle = exception.GetType().Name;
var randomEventId = GetEventId();
var eventId = new EventId(randomEventId, eventTitle);
var errorException = new ErrorException(Error.CreateId(eventId.ToString()), HttpStatusCode.InternalServerError, Error.CreateNewId(), eventTitle, exception.Message, exception.InnerException);
errorsDocument.AddError(errorException);
return httpContext.Response.WriteAsync(errorsDocument.ToJson());
}
} |
@circleupx Thank you for the information. What you are saying makes sense as they are now generalizing (in 3.1) ASP.NET Core and MVC is an "extension". I'll look into this further but I'll deprecate the exception filter in favor of exception middleware for the sample and my production projects as well. Thanks again! |
Thank you all for your support! |
Nice, can't wait to see how you handled PATCH. My use case was simple, so I used another library, SimplePatch. See if that helps you. |
Sorry.. late by more than one week later. This library is very powerful but we still have to do a lot of manual work. |
I just took a look at the SimplePatch library. Basically it's what I was trying to do. What do you think about it @scott-mcdonald ? |
@RichardsonWTR @circleupx I have not had a chance to look at PATCH yet due to current job pressures, BUT coincidentally we will need PATCH soon for our API so I will be taking a close look at this soon as well. Thanks for the pull request... |
That is going to be an awesome feature for JsonApiFramework. It would help me remove my dependency on SimplePatch. Speaking of SimplePatch, I like their configuration approach, it gives developers the power to control how their models are mapped. It would be awesome to have something similar in JsonApiFramework. Let me know if I can help. |
@RichardsonWTR Sorry I have not had time to look at the PATCH implementation that is being suggested in the JsonApiFramework samples yet as I have been so damn busy my head is spinning. Currently where in my job where I have needed PATCH in a few places I have done a manual implementation which is not what I want in the end. I will get to this eventually but probably not until we have a releasable product at my work... So sorry... |
Imagine this situation: the client of the project Blogging Tutorial from the samples repo sends this message to the server:
In the samples repo the post/put/delete functions aren't implemented:
I was trying to insert a new element by transforming a
Document
object to aArticle
object with the relationships.This is a piece of code of the implementation I was trying.
What do you think? Am I in the right path?
Any help would be appreciated.
Regards
The text was updated successfully, but these errors were encountered: