This is what was missing. At the moment such parameters are appended at the end of the argument list which won't work if there are default values. We will try to fix that without introducing a breaking change where possible.
We will make another change - the http client registration will use Configuration where possible (in server apps or when proxy is enabled):
builder.Services.AddHttpClient("PetStore", client => client.BaseAddress = new Uri(builder.Configuration["PetStore.Url"]));
This should prevent the other issue from happening.
That sounds great! Thanks a lot!
Hi @korchev ,
thanks for implementing this feature, however, there is still at least one big issue it:
The service URL is now removed from the code which is great but when I perform an infer of the service, the URL gets (over)written in ALL (!) config files, so it overwrites the different URLs in my environment-specific config files. This is not the way it should be...
Another thing is that it is a bit hard to add extra parameters (e.g. default headers for an API key etc.) to the new AddHttpClient line since this line gets overridden as well. I have to retrieve the HttpClient service afterwards manually to add a default headers. However, this is still something I can deal with. Maybe you can modify your code that replaces/adds the line:
builder.Services.AddHttpClient("PetStore"
in a way that it only checks if it starts with this string and does not compare the entire string (in case you do string parsing on that!).
Thanks a lot!
This shouldn't happen. Radzen Blazor Studio is supposed to update only the developer appsettings.json file. Please describe in more details what your current setup is.
I am afraid this is not possible.
Well I have a defaut appsettings.json in the project root and two additional configs for my environments: appsettings.Test.json and appsettings.Production.json
The service URL is getting overwritten in all 3 files when I do an infer.
And something else you might want to consider: When calling an API web method which fails fails, you raise an exception by invoking response.EnsureSuccessStatusCode(); - but since this raises and exception, the only information you get from the exception object is the status code. In many cases, the reponse may also contain additional information (reponse text). However, the reponse object is not accessable any more since OnPostXXXXXResponse(response); is never called in this case.
If you change the generated code to this, the OnPost method will also be called in case of an error and it is possible to access the response object:
var response = await httpClient.SendAsync(request);
try
{
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
throw ex;
}
finally
{
OnPostXXXXXResponse(response);
}
That would help for sure!
I reproduced the problem. The correct behavior should have been to only update appsettings.Development.json (if it exists). We would address that with the next release.
Thanks a lot. Any thoughts about the proposal with the exception handling (from my last post)?
I am not quite sure I understand it. What is the desired outcome? How do you plan to use that?
Let's assume a web service method returns an non-success status code e.g. 405. Many systems just do not respond with the HTTP error code but also return an error text that helps you to understand what the actual problem is.
The code you generate gets this reponse and then performs a EnsureSuccessStatusCode() - which will raise an exception here that I can catch. However, this exception only contains the status code and not the error text from the response.
Normally this would not be a problem since we have the OnPostXXXXXResponse(response) method that can access the response object after the web method has been called for further handling.
But in case of non-success status codes, this method does not get called since the EnsureSuccessStatusCode() raises an exception and terminates instantly.
With the modification I suggested, the OnPostXXXXXResponse(response) method will also be called if the response has a non-success status code. That way, I can e.g. get the text from the reponse to know what the actual problem is.
This makes sense. Thank you for elaborating. We will try to squeeze it for the next release.
Thanks for the integration in the latest release. I was able to use a partial class to make the required adjustments to the generated file, which are now persisted due to using the partial class.
However, I noticed a strange behaviour: If I add the following code (actually the method "SendFinishedSignal") into my partial class, infering the API does not work any more. In fact opening the dialog where I can select the data source does not work - it just shows a loading/progress bar which runs forever. As soon as I remove the code from my partial class, I can again select my API as data source and do an infer:
private TaskCompletionSource<HttpResponseMessage> _responseCompletionSource;
private void SendFinishedSignal(HttpResponseMessage response)
{
_responseCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
_responseCompletionSource.SetResult(response);
}
The problem even occurs if I do not use the method anywhere in my code. The method declaration itself seems not to be the problem: If I remove the two lines of code from the method, it also works.
Any idea what might cause this problem?
Probably the code which infers a data source causes the problem. We can tell more if you send us your app to info@radzen.com.
Hi,
no need to create a project to reproduce this. Just take the PetStore project from your sample projects and add a C# file "PetStoreServiceEx.cs" with the following code in the "Services" folder next to the generated "PetStoreService.cs":
namespace RepoTest
{
public partial class PetStoreService
{
private TaskCompletionSource<HttpResponseMessage> _responseCompletionSource;
private void SendFinishedSignal(HttpResponseMessage response)
{
_responseCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
_responseCompletionSource.SetResult(response);
}
}
}
That's all. If you now do an infer of the PetStoreService in Radzen, you can reproduce the issue (using the latest version von Radzen of course!).
Thanks! Will be fixed for the next release. As a workaround you can wrap this method with #if !RAZEN
:
#if !RADZEN
private void SendFinishedSignal(HttpResponseMessage response)
{
_responseCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
_responseCompletionSource.SetResult(response);
}
#endif
Hi @korchev,
thanks a lot, that helps. However, I think I found yet another bug. This time, it is again about parameter ordering and nullable data types:
Here is my "original" web service method declaration:
public async Task<IActionResult> GenerateKey([BindRequired] KeyGenType type, [BindRequired] string licensee, [BindRequired] string activationCode, bool isSaaS, DateTime? expirationDate)
Radzen consumes this method and generates this API method:
public async Task<string> GetKeyGeneratorGenerateKey(string type, string licensee, string activationCode, bool? isSaaS = default, DateTime expirationDate = default)
As you can see, the last DateTime parameter which is nullable is not nullable any more in the end but the bool parameter suddenly becomes nullable for no reason.
Would be great if you could fix this as well since this currently prevents me from using the Webservice data source feature in Radzen.
Thanks!
Please send us the json schema so we can test
Here you are:
{
"openapi": "3.0.1",
"info": {
"title": "WebApplication1 | v1",
"version": "1.0.0"
},
"servers": [
{
"url": "https://localhost:7142/"
}
],
"paths": {
"/Test/GenerateKey": {
"get": {
"tags": [
"Test"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"type": "integer"
}
},
{
"name": "licensee",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "activationCode",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "isSaaS",
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "expirationDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
},
"application/json": {
"schema": {
"type": "string"
}
},
"text/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
},
"components": { },
"tags": [
{
"name": "Test"
}
]
}
This isSaaS parameter is not listed as required in the JSON which is why Radzen Blazor Studio makes a nullable boolean out of it. We treat all non required parameters this way. The problem is that DateTime should have been made nullable as well. We will change that with the next release. Radzen Blazor Studio will generate this code:
public async Task<string> GetKeyGeneratorGenerateKey(string type, string licensee, string activationCode,
bool? isSaaS = default, DateTime? expirationDate = default)