hassanhabib / restfulsense Goto Github PK
View Code? Open in Web Editor NEWA RESTFul operations client that serializes responses and throws meaningful exceptions for >= 400 status codes.
Home Page: https://www.restfulsense.com
A RESTFul operations client that serializes responses and throws meaningful exceptions for >= 400 status codes.
Home Page: https://www.restfulsense.com
Replace file path in the markdown header logo with the following.
(https://raw.githubusercontent.com/hassanhabib/RESTFulSense/master/RESTFulSense/images/rs_git_logo.png) to assure that when the next nuget package publish logo image is corrected.
Currently if you want to create exceptions for unit tests, when you are using WebAssembly
library, you have to do the following:
public static TheoryData CriticalDependencyExceptions()
{
string someMessage = GetRandomMessage();
var someResponseMessage = new HttpResponseMessage();
return new TheoryData<Xeption>()
{
new HttpResponseUrlNotFoundException(
someResponseMessage,
someMessage),
new HttpResponseUnauthorizedException(
someResponseMessage,
someMessage),
new HttpResponseForbiddenException(
someResponseMessage,
someMessage)
};
}
At the same time Core project made it easier to create the above exceptions without response message and message.
The IRESTFulApiFactoryClient
currently has no overloads to support using a CancellationToken
in API calls like the normal HttpClient, e.g. HttpClient.GetAsync(String, CancellationToken).
I'd like to use it like such: this.restfulApiClient.GetContentAsync<T>(relativeUrl, cancellationToken)
.
Initialize, Setup, Implement Acceptance Test Project with WireMock.Net for the RESTFulSense project
Currently we cannot construct a multipart-form for a model which has null or empty value properties.
public class Person
{
[RESTFulStringContent(name: "firstName")]
public string FirstName { get; set; }
[RESTFulStringContent(name: "middleName")]
public string? MiddleName { get; set; }
}
async Task MakeRequestAsync()
{
var person = new Person
{
FirstName = "John Smith",
MiddleName = null
};
var httpClient = SetupHttpClient();
var apiClient = new RESTFulApiFactoryClient(httpClient);
var response = await apiClient.PostFormAsync<Person, string>("/users", person);
}
Since no value was provided for middleName
property, the next exception is thrown:
RESTFulSense.Models.Coordinations.Forms.Exceptions.FormCoordinationDependencyValidationException : Form coordination dependency validation error occurred, fix the errors and try again.
---- RESTFulSense.Models.Orchestrations.Forms.Exceptions.FormOrchestrationDependencyValidationException : Form orchestration dependency validation error occurred, fix the errors and try again.
-------- RESTFulSense.Models.Foundations.Forms.Exceptions.FormValidationException : Form validation error occurred, fix errors and try again.
------------ RESTFulSense.Models.Foundations.Forms.Exceptions.InvalidFormArgumentException : Invalid form arguments.Please fix the errors and try again.
Stack Trace:
at RESTFulSense.Services.Coordinations.Forms.FormCoordinationService.TryCatch(ReturningMultipartFormDataContentFunction returningMultipartFormDataContentFunction)
at RESTFulSense.Services.Coordinations.Forms.FormCoordinationService.ConvertToMultipartFormDataContent[T](T object)
at RESTFulSense.Clients.RESTFulApiFactoryClient.PostFormAsync[TContent, TResult](String relativeUrl, TContent content, CancellationToken cancellationToken)
// ...
Next overloads on the IRESTFulApiFactoryClient
allow for scenarios where multipart form-data payload can be sent to the server and still have the response validation is place.
In some scenarios, it is useful to return the HttpResponseMessage
directly and allow the client to deserialize the response. In other scenarios, the TResponse
is the expected type of deserialization.
namespace RESTFulSense.Clients
{
public partial interface IRESTFulApiFactoryClient
{
ValueTask<HttpResponseMessage> PostContentAsync(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<TResponse> PostContentAsync<TResult>(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<HttpResponseMessage> PutContentAsync(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<TResponse> PutContentAsync<TResult>(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
}
}
namespace RESTFulSense.WebAssembly.Clients
{
public partial interface IRESTFulApiFactoryClient
{
ValueTask<HttpResponseMessage> PostContentAsync(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<TResponse> PostContentAsync<TResult>(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<HttpResponseMessage> PutContentAsync(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
ValueTask<TResponse> PutContentAsync<TResult>(string relativeUrl, HttpContent content, CancellationToken cancellationToken = default);
}
}
When controllers are configured to use Camel Case for JSON Serialization, it correctly returns the data, but when an exception is thrown, the properties under errors are returned with Pascal Case convention.
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Invalid city. Please correct the errors and try again.",
"status": 400,
"errors": {
"Name": [
"Text is required"
],
"CreatedDate": [
"Date is required",
"Date is not recent"
],
"CreatedByUser": [
"Text is required"
],
"UpdatedDate": [
"Date is required"
],
"UpdatedByUser": [
"Text is required"
]
}
}
We need to consider adding an option to supporting In-Memory or Redis caching integration out of the box with RESTfulSense.
Here's a scenario:
Consider a system that makes several calls to the same API with the same parameters in less than 1 second. And optimization effort here can be done to offload some of the latency cost on the API consumer side.
Here's how I visualize this to look like:
private async ValueTask<T> GetAsync<T>(string relativeUrl) =>
await this.apiClient.GetContentAsync<T>(relativeUrl, enableCaching: true, cacheInvalidation: 1000);
private readonly IRESTFulApiFactoryClient apiClient;
public ApiBroker(IRESTFulApiFactoryClient apiClient)
{
this.apiClient = apiClient;
this.apiClient.ConfigureCache(enableCaching: true, cacheInvalidation: 1000);
}
public interface IRESTFulSenseCaching
{
ValueTask<T> TryGetCachedValue(string key);
}
public ApiBroker(IRESTFulApiFactoryClient apiClient, IRESTFulSenseCache externalCacheSource)
{
this.apiClient = apiClient;
this.apiClient.ConfigureCache(enableCaching: true, cacheInvalidation: 1000, source: externalCacheSource);
}
Implement the following function:
ValueTask PostContentAsync<T>(string relativeUrl, T content, string mediaType = "text/json");
This capability will support scenarios where the API doesn't necessarily return anything.
https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-semantics#section-15.3.5
I have a method get request that has a parameter and some time this parameter has no value, so the default value is empty string or null:
public async ValueTask<List<Order>> GetOrdersAsync(string properties = null) => await this.GetAsync<List<Order>>($"{OrderUrl}/includeProperties/{properties}");
But I got error and it doesn't hit the end point of my controller!
Thanks for help.
Some APIs use PATCH instead of PUT and RESTFulSense should support that. Currently, neither client in RESTFulSense exposes PATCH methods.
If the serialization goes wrong, for example, when the value to be deserialized is outside the valid range of the target datatype, the library will throw a JsonSerializationException
which will leak to the customer. But this exception should be wrapped by the library.
Currently, interfaces IRESTFulApiClient
and IRESTFulApiFactoryClient
define the public
access modifier on some of the methods.
Remove public
access modifiers from the next interfaces:
RESTFulSense.Clients.IRESTFulApiClient
RESTFulSense.Clients.IRESTFulApiFactoryClient
RESTFulSense.WebAssembly.Clients.IRESTFulApiClient
RESTFulSense.WebAssembly.Clients.IRESTFulApiFactoryClient
seems in net standard 2.1 there is no cancellation token for some methods
in net 8.0 it´s added cancellation token, so RESTFulSense could be upgraded to NET 8. What do you think about this @cjdutoit @hassanhabib ? The drawback is back compatibility for those who still doesn´t use NET 8 in their projects.
I had a Microsoft.Data.SqlClient.SqlException bubble up to the controller which then threw the following exception:
System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Collections.Generic.List``1[System.String]'.'
The exception had the following Data:
{ "HelpLink.ProdName", "Microsoft SQL Server" }
{ "HelpLink.ProdVer", "15.00.2080" }
{ "HelpLink.EvtSrc", "MSSQLServer" }
{ "HelpLink.EvtID", "208" }
{ "HelpLink.BaseHelpUrl", "https://go.microsoft.com/fwlink" }
{ "HelpLink.LinkId", "20476" }
i.e. A Dictionary<string, string>
rather than a Dictionary<string, List<string>>
.
Happy to submit a PR if it's something you want to handle.
While Acceptance and Integration testing using PostContentAsync method, I was going through the source code and I see a dependency to serialize using Newtonsoft JsonSerializerSettings (in ConvertToJsonStringContent within RESTFulApiFactoryClient).
In case the underlying object uses System.Text.Json JsonPropertyName Attribute . Those json settings are ignored, and I get BadRequest Error..
I tried handcrafting the object using ExpandoObject() with desired serialized property names and it worked.
In case the underlying "Type" uses System.Text.Json then serialization should honor System.Text.Json serialization logic instead of defaulting to Newtonsoft
I get an error Self referencing loop detected for property 'X' with type 'XX'
.
My method in ApiBroker:
private async ValueTask<T> PutAsync<T>(string relativeUrl, T content)
{
return await apiCient.PutContentAsync(relativeUrl, content);
}
Before I used System.Text.Json
and to go around this error, I just create JsonSerializerOptions
where ReferenceHandler = ReferenceHandler.IgnoreCycles
and then I send this option with PutAsJsonAsync
like this:
JsonSerializerOptions options = new() { ReferenceHandler = ReferenceHandler.IgnoreCycles };
await client.PutAsJsonAsync(url, obj, options);
My question:
How can I add this option in PutAsync
?
Thanks.
Swagger fails since some methods inside RESTFulSense.Controller
class are public but have no attributes on them.
Error Message:
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action - XXXXXXXXX.YYYYController.BadRequest. Actions require an explicit HttpMethod binding for Swagger/OpenAPI 3.0
Hi,
I didn't experiment with it, but I thought instead of overriding the entire HttpClient
, we might be able to create a DelegatingHnalder
etc. that manages it all for us.
The reason is so that we don't block services like from IHttpClientFactory
etc.
I just fixed a problem with my library due to this, where text/json isn't accepted and silently fails.
It seems application/json is the offical MIME type. I saw support was added for this recently, but not as default.
https://stackoverflow.com/questions/477816/which-json-content-type-do-i-use
Currently when trying to construct a multipart-form for a model which has properties of another type except for string type, the next exception is thrown: Unable to cast object of type 'System.Decimal' to type 'System.String'
.
public class Person
{
[RESTFulStringContent(name: "firstName")]
public string FirstName { get; set; }
[RESTFulStringContent(name: "salary")]
public decimal Salary { get; set; }
}
async Task MakeRequestAsync()
{
var person = new Person
{
FirstName = "Jane Smith",
Salary = 10_500M
};
var httpClient = SetupHttpClient();
var apiClient = new RESTFulApiFactoryClient(httpClient);
var response = await apiClient.PostFormAsync<Person, string>("/users", person);
}
Exception thrown:
RESTFulSense.Models.Client.Exceptions.RESTFulApiClientDependencyException : Form coordination dependency error occurred, fix the errors and try again.
---- RESTFulSense.Models.Orchestrations.Forms.Exceptions.FormOrchestrationServiceException : Form orchestration service error occurred, contact support.
-------- RESTFulSense.Models.Orchestrations.Forms.Exceptions.FailedFormOrchestrationServiceException : Failed form orchestration service occurred, please contact support
------------ System.InvalidCastException : Unable to cast object of type 'System.Decimal' to type 'System.String'.
Some APIs use PATCH instead of PUT and RESTFulSense should support that. Currently.
Currently, we are only handling httpResponseException
. However, some api are also throwing httpRequestException
(for example, Azure AAD, while login). So, come up with purposing, modellling and simulation of this feature. Once approved here, you can start coding!
Please see this Video from May 26 Deep Dive: I linked to the time relevant to this fix.
As current RESTFulSense version uses NewtonSoft, there are some scenarios in our applications which
This feature allows us to take care about the serialization/deserialization process from outside the library.
This solution is a workaround to deal with different serializations libraries allowing the power and true value of RestFulSense to be used regardless of the serializer you want to use.
A better approach would be to implement SPAL inside RESTFulSense and add as many serialization implementations we need as new packages.
The Json Serializer setting is for "DefaultValueHandling". This would better reflect the parameters intent.
Required for OpenAI response.
To prevent over posting we should be able to select not posting null value properties.
We need to investigate how to support a model that gets converted under the hood into StreamContent
and StringContent
Hi!
I have a problem when posting a request to an endpoint. Deserializing the response causes
Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
It´s curious if I run the post from Postman with the exact input json I retrieved the output json. It seems correct
So I don´t know what´s trying to deserialize.
I try to import all the RESTFulSense project to debug it without success because wrong references between NetStandard RESTFulSense project conflicts with my .Net6 project so not able to compile. I tried adding some Console.WriteLine on responseString inside DeserializeResponseContent but i don´t know why something it´s blocking the messages to console. I tried too some static Action to "listen" the value from my broker but MissingMethodException at runtime.
Any suggestion?
Thanks in advance!
Is there a plan of making this work with .NET6 minimal APIs?
Hello,
Is it possible to add (or maybe there is) a way to set the Authorization
request header to the httpClient
inside RESTFulApiFactoryClient
Mybe something like that:
public void SetAuthenticationHeaderValue(AuthenticationHeaderValue authenticationHeaderValue)
=> this.httpClient.DefaultRequestHeaders.Authorization = authenticationHeaderValue;
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.