RestSharp asynchronous PUT - c#

There is one application, the API with access to the database and one application that calls the API with RestSharp.
I implemented all async methods of RestSharp to work generic. So GET, POST, DELETE are all working.The only one I can't get to work is the PUT.
First of all this is my controllers PUT:
[HttpPut("{id}")]
public void Put(int id, [FromBody]ApplicationUser value)
{
string p = value.Email;
}
this is my method:
public Task<bool> PutRequestContentAsync<T>(string resource, object id, T resourceObject) where T : new()
{
RestClient client = new RestClient("http://localhost:54008/api/");
RestRequest request = new RestRequest($"{resource}/{{id}}", Method.PUT);
request.AddUrlSegment("id", id);
request.AddObject(resourceObject);
var tcs = new TaskCompletionSource<bool>();
var asyncHandler = client.ExecuteAsync<T>(request, r =>
{
tcs.SetResult(r.ResponseStatus == ResponseStatus.Completed);
});
return tcs.Task;
}
and this is my call in a view (all other calls of GET,... are working fine):
bool putOk = await new RepositoryCall()
.PutRequestContentAsync("Values", 2,
new ApplicationUser {
Email="test#xxxxxxx.de"
}
);
with debugging, the response-status is Completed but the PUT is never called.
Any idea what the problem could be?

So finally I got my answer myself... (sit yesterday 6 hours and no result, today one more hour and it works)
public Task<bool> PutRequestContentAsync<T>(string resource, object id, T resourceObject) where T : new()
{
RestClient client = new RestClient("http://localhost:54008/api/");
RestRequest request = new RestRequest($"{resource}/{{id}}", Method.PUT);
request.AddUrlSegment("id", id);
request.RequestFormat = DataFormat.Json;
request.AddBody(resourceObject);
var tcs = new TaskCompletionSource<bool>();
var asyncHandler = client.ExecuteAsync<T>(request, (response) => {
tcs.SetResult(response.ResponseStatus == ResponseStatus.Completed);
});
return tcs.Task;
}
the trick was to add a RequestFormat and changing AddObject to AddBody :)

Related

Flutter with GetX, get connect don't calling to the setted adress

I'm trying to get a response from a web api(C#), but, in a specific method, the end point address is pointed to the port the front end is running on, but in the others methods it's working as well.
Future<void> onInit() async {
httpClient.baseUrl = await baseUrlGetter();
httpClient.addRequestModifier((Request request) {
request.headers['Accept'] = 'application/json';
request.headers['Content-Type'] = 'application/json';
request.headers['origemAcesso'] = 't2painel';
return request;
});
httpClient.addAuthenticator((Request request) {
var token = _storageService.token;
var headers = {'Authorization': "Bearer $token"};
request.headers.addAll(headers);
return request;
});
super.onInit();
}
Ahead, it'ts the onInit method in API class, the function baseUrlGetter is working, returnig the rigth url of the API.
But, in the method below:
Future<ImageModel> fetchDistribuidorImage() async {
var response = _errorHandler(await get('/api/Imagens/DistribuidorImagem'));
var data = ImageModel.fromJson(response.body);
return data;
}
Take a look to this images (read the images descriptions)
Only in the method "fetchDistribuidorImage" is not correct.

In C#, using a loan pattern, TryAsync, and RestClient.ExecuteAsync(), how can I get the system to wait for the result of the second callout?

I'm currently refactoring a microservice which uses RestSharp's RestClient to call out to Personio, in order to use the latest version of RestSharp (v107), as well as to use ExecuteAsync instead of Execute.
I have the following method:
[SuppressMessage("Style", "IDE0053:Use expression body for lambda expressions", Justification = "lambda leave Match ambiguous.")]
public TryAsync<T> WithAuthorization<T>(Func<Token, Task<T>> doSomething, CancellationToken cancellationToken) =>
TryAsync(async () =>
{
T? result = default;
Exception? resultException = null;
TryAsync<Token> authorizationAttempt = TryAuthorize(cancellationToken);
_ = await apply(token => doSomething(token), authorizationAttempt)
.Match(
Succ: async dataTask =>
{
result = await dataTask;
},
Fail: exception =>
{
resultException = exception;
}
)
.ConfigureAwait(false);
// FIXME: Does not wait!!!!
return result
?? ((resultException == null)
? throw new PersonioRequestException("Could not get data from Personio: Reason unknown.")
: throw new PersonioRequestException($"Could not get data from Personio: {resultException.Message}", resultException)
);
});
As indicated in the code above, the method does not wait before returning the result, or as the case happens to be throwing an exception with reason unknown.
With the debugger, I've been able to determine authorizationAttempt gets a value and doSomething() is invoked, but while waiting for a response, the error is thrown.
The code which consumes the above method, providing the function for doSomething() is this:
public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options, CancellationToken cancellationToken) =>
_authorizationClient.WithAuthorization<RequestResponse<T>>(
async token =>
{
UriBuilder urlBuilder = new(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint))
{
// This is used to add parameters which are used for filtering and may be unique for the record type, such as "updated_from".
Query = options.QueryParameters.ToString()
};
RestRequest request = new(urlBuilder.Uri, Method.Get);
request.Timeout = -1;
if (options.Pagination.IsActive)
{
request = request.AddQueryParameters(options.Pagination);
}
request = request
.AddHeader("Accept", "application/json")
.AddHeader("Authorization", $"Bearer {token.Value}");
return await GetRecords<T>(request, cancellationToken);
},
cancellationToken
);
private async Task<RequestResponse<T>> GetRecords<T>(RestRequest request, CancellationToken cancellationToken)
{
RestResponse<RequestResponse<T>> requestResponse = await _restClient.ExecuteAsync<RequestResponse<T>>(request, cancellationToken);
// FIXME: The next line is never executed.
RequestResponse<T>? dataResponse = JsonConvert.DeserializeObject<RequestResponse<T>>(requestResponse?.Content ?? "");
return (requestResponse?.IsSuccessful ?? false)
? (dataResponse != null && dataResponse.WasSuccessful)
? dataResponse
: throw new PersonioRequestException("Connected to Personio, but could not get records.")
: throw (
(requestResponse?.ErrorException != null)
? new("Could not get records from Personio.", requestResponse.ErrorException)
: new($"Could not get records from Personio. {dataResponse?.Error?.Message ?? UnknownProblem}."));
}
As indicated in the code above, the method GetRecords() is invoked, but the system throws an error from result (back in the first method above) being unpopulated before ExecuteAsync() has any sort of result.
An earlier form of the code, with an earlier version of RestSharp(v106) and synchronous execution worked fine. At that time, the TryGet looked like:
public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options) =>
_authorizationClient.WithAuthorization<RequestResponse<T>>(token =>
{
UriBuilder urlBuilder = new(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint))
{
// This is used to add parameters which are used for filtering and may be unique for the record type, such as "updated_from".
Query = options.QueryParameters.ToString()
};
_restClient.BaseUrl = urlBuilder.Uri;
_restClient.Timeout = -1;
IRestRequest request = new RestRequest(Method.GET);
if (options.Pagination.IsActive)
{
request = request.AddQueryParameters(options.Pagination);
}
request = request
.AddHeader("Accept", "application/json")
.AddHeader("Authorization", $"Bearer {token.Value}");
return Task.FromResult(GetRecords<T>(request));
});
private RequestResponse<T> GetRecords<T>(IRestRequest request)
{
IRestResponse<RequestResponse<T>> requestResponse = _restClient.Execute<RequestResponse<T>>(request);
RequestResponse<T>? dataResponse = JsonConvert.DeserializeObject<RequestResponse<T>>(requestResponse.Content);
return requestResponse.IsSuccessful
? (dataResponse != null && dataResponse.WasSuccessful)
? dataResponse
: throw new PersonioRequestException("Connected to Personio, but could not get records.")
: throw (
(requestResponse.ErrorException != null)
? new("Could not get records from Personio.", requestResponse.ErrorException)
: new($"Could not get records from Personio. {dataResponse?.Error?.Message ?? UnknownProblem}."));
}
What am I doing wrong or missing?
How can I make this work using RestSharp 107.x and ExecuteAsync()?
With thanks to #AlexeyZimarev, I was able to get the TryGet() method working.
It now looks like:
public TryAsync<RequestResponse<T>> TryGet<T>(RequestOptions options, CancellationToken cancellationToken) =>
TryAsync(async () =>
{
_restClient.Authenticator = _authorizationClient;
Uri uri = new UriBuilder(_personioConfig.BaseUrl.AppendPathSegment(options.Endpoint)).Uri;
RestRequest request = new RestRequest(uri, Method.Get)
.AddQueryParameters(options.QueryParameters);
if (options.Pagination.IsActive)
{
request = request.AddQueryParameters(options.Pagination);
}
request.Timeout = -1;
return await GetRecords<T>(request, cancellationToken);
});
The line _restClient.Authenticator = _authorizationClient; does not very safe here, since it is being assigned on an injected instead of RestClient, which in the future might be used elsewhere, but I "solved" this by making the client transient instead of a singleton.
I also removed all the TryAsync from PersonioAuthorizationClient since it didn't seem to play well with whatever AuthenticatorBase is doing.

How to stop a Httpclient in c#

In my wpf project I have this:
private async void RequestStart(HttpClient client, Task task)
{
NetworkModel nmodel = new NetworkModel();
NetworkModel1.Rootobject tryModel = await nmodel.ClientStock();
var apiClient = new APIClient();
Task<string> getResponseColourSize = apiClient.GetAsync($"https://www.try.com/{id}.json");
var ATCvalues = new Dictionary<string, string>
{
{ "style", sizeColourID.colourID.ToString() },
{ "size", sizeColourID.sizeID.ToString() },
};
var ATCcontent = new FormUrlEncodedContent(ATCvalues);
var ATCresponse = await client.PostAsync($"https://www.try.com/{id}/cart.json", ATCcontent);
var ATCresponseString = await ATCresponse.Content.ReadAsStringAsync();
System.Windows.MessageBox.Show(ATCresponseString);
}
Firstly I make a GET request and then after several other GET requests, I make a POST request
How would I be able to make it so that on a button click the user would be able to stop the request. The problem is that I cannot find something online to satisfy this. The only things I have found are for either GET or POST requests. Would I just need to stop the RequestStart altogether? Any help would be appreciated!

Task.Run sometimes returns twice

I have an azure function app that I call from a slack slash command.
Sometimes the function takes a little while to return the data requested, so I made that function return a "Calculating..." message to slack immediately, and run the actual processing on a Task.Run (the request contains a webhook that I post back to when I finally get the data) :
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
This works mostly fine, except every now and then when people are calling the function from slack, it will return the data twice. So it will show one "Calculating..." message and then 2 results returned from the above function.
BTW, Azure functions start with :
public static async Task
Thanks!
UPDATE : here is the code for the function:
[FunctionName("QuoteCheck")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestMessage req, TraceWriter log, ExecutionContext context)
{
var opsHelper = new OpsHelper();
string bodyContent = await req.Content.ReadAsStringAsync();
var parsedBody = HttpUtility.ParseQueryString(bodyContent);
var commandName = parsedBody["command"];
var incomingBrandId = parsedBody["text"];
int.TryParse(incomingBrandId, out var brandId);
var responseUrl = parsedBody["response_url"];
var incomingData = new IncomingSlackRequestModel
{
UserName = parsedBody["user_name"],
ChannelName = parsedBody["channel_name"],
CommandName = commandName,
ResponseUri = new Uri(responseUrl),
BrandId = brandId
};
var opsData = OpsDataFactory.GetOpsData(context.FunctionAppDirectory, environment);
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
// Generate a "Calculating" response message based on the correct parameters being passed
var calculatingMessage = opsHelper.GenerateCalculatingMessage(incomingData);
// Return calculating message
return req.CreateResponse(HttpStatusCode.OK, calculatingMessage, JsonMediaTypeFormatter.DefaultMediaType);
}
}
And then the GenerateQuoteCheckMessage calculates some data and eventually posts back to slack (Using Rest Sharp) :
var client = new RestClient(responseUri);
var request = new RestRequest(Method.POST);
request.AddParameter("application/json; charset=utf-8", JsonConvert.SerializeObject(outgoingMessage), ParameterType.RequestBody);
client.Execute(request);
Using Kzrystof's suggestion, I added a service bus call in the function that posts to a queue, and added another function that reads off that queue and processes the request, responding to the webhook that slack gives me :
public void DeferProcessingToServiceBus(IncomingSlackRequestModel incomingSlackRequestModel)
{
var serializedModel = JsonConvert.SerializeObject(incomingSlackRequestModel);
var sbConnectionString = ConfigurationManager.AppSettings.Get("SERVICE_BUS_CONNECTION_STRING");
var sbQueueName = ConfigurationManager.AppSettings.Get("OpsNotificationsQueueName");
var client = QueueClient.CreateFromConnectionString(sbConnectionString, sbQueueName);
var brokeredMessage = new BrokeredMessage(serializedModel);
client.Send(brokeredMessage);
}

HttpClient PostAsync not working in CRM plugin

I am trying to send json to a web API using HttpClient.PostAsync. It works from a console application but not from my CRM plugin. Doing some research I noted that it is probably something to do with the context the plugin runs in and threading. Anyway here is my calling code:
public async void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.InputParameters.Contains("Target"))
{
if (context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "new_product")
{
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
if (entity.Contains("new_begindate") && entity.Contains("new_expirationdate"))
{
await OnlineSignUp(entity, service);
}
}
catch (InvalidPluginExecutionException)
{
throw;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(OperationStatus.Failed, "Error signing up: " + e.Message);
}
}
}
}
}
And here is the relevant code for sending the json:
private async Task<HttpResponseMessage> OnlineSignUp(Entity license, IOrganizationService service)
{
...
var json = JsonConvert.Serialize(invitation);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "token=7d20f3f09ef24067ae64f4323bc95163");
Uri uri = new Uri("http://signup.com/api/v1/user_invitations");
var response = await httpClient.PostAsync(uri, content).ConfigureAwait(false);
int n = 1;
return response;
}
}
The exception is thrown with a message "Thread was being aborted". Can anyone tell me what I am doing wrong?
I would guess this is going to fail somewhat randomly based on use of async/await. I wouldn't think CRM really supports plugins returning control before they are complete. When it fails, it looks like the thread was in the process of being cleaned up behind the scenes.
CRM is already handling multi-threading of plugins and supports registering plugin steps as asynchronous if they are long running (or don't need to be run in the synchronous pipeline). It would make more sense to use a synchronous HTTP call here like:
var response = httpClient.PostAsync(uri, content).Result;
EDIT: To illustrate, this is an overly-trivialized example of what is most likely happening when CRM goes to kickoff your plugin and you're using async/await (from LinqPad).
static async void CrmInternalFunctionThatKicksOffPlugins()
{
var plugin = new YourPlugin();
//NOTE Crm is not going to "await" your plugin execute method here
plugin.Execute();
"CRM is Done".Dump();
}
public class YourPlugin
{
public async void Execute()
{
await OnlineSignUp();
}
private async Task<HttpResponseMessage> OnlineSignUp()
{
var httpClient = new HttpClient();
var r = await httpClient.PostAsync("http://www.example.com", null);
"My Async Finished".Dump();
return r;
}
}
Which will print:
CRM is Done My Async Finished
looks like you are using Json.NET, when you use external assemblies there are some things to take care of (merging) and not always the result works.
Try to serialize using DataContractJsonSerializer
example: http://www.crmanswers.net/2015/02/json-and-crm-sandbox-plugins.html

Categories