Flutter web: How to upload Image to ASP .NET Core web API? - asp.net-core

I have an API which is for a CMS that can change my entire 'AboutUs' web page. The following was the Data Model that holds all the contents of that 'About Us' webpage.
I have a Future method that goes this way to update the 'About Us' webpage contents within database only. (via ASP.Net Core web API)
Future updateAboutUsContent() async
Future<AboutUsContextModel> updateAboutUsContent(
String title,
String context,
String subcontext,
PlatformFile imageId,
) async {
final abUC =
await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
headers: <String, String>{
"Content-Type": "multipart/form-data;charset=UTF-8",
},
body: jsonEncode(<String, dynamic>{
"title": title,
"context": context,
"subContext": subcontext
}));
final request = http.MultipartRequest(
"POST", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
request.files.add(http.MultipartFile(
"imageFile",
imageId.readStream,
imageId.size,
filename: imageId.name.split("/").last);
var resp = await request.send();
String response = await resp.stream.bytesToString();
print("==================\nThis is the response:\n=================" +
response);
}
if (abUC.statusCode == 200) {
//If the server did return a 200 Created All green post
//then parse the JSON
return AboutUsContextModel.fromJson(jsonDecode(abUC.body));
} else if (abUC.statusCode == 400) {
throw Exception('Error code was 400. About Us Content Not Foun');
} else {
throw Exception('Nothing About Us Content created in Flutter');
}
}
And This Future will call the ASP web API which is as below:
[HttpPut("{id}")]
public async Task<ActionResult<AboutUsContextModel>> PutAboutUsContent(int id, string title, string context, string subcontext, IFormFile imageFile)
{
AboutUsContextModel abUC = await _context.AboutUsContextModel.Include(lim => lim.Image).FirstOrDefaultAsync(limy => limy.AboutUs_Context_Id == id);
if(abUC == null)
{
return BadRequest("No such About Us Content!");
}
if(imageFile != null)
{
ImageModel imgfrmDB = abUC.Image;
if(imgfrmDB != null)
{
string cloudDomaim = "https://privacy-web.conveyor.cloud";
string uploadDrcty = _webEnvr.WebRootPath + "\\Images\\";
if (!Directory.Exists(uploadDrcty))
{
Directory.CreateDirectory(uploadDrcty);
}
string filePath = uploadDrcty + imageFile.FileName;
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await imageFile.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
using (var memoryStream = new MemoryStream())
{
await imageFile.CopyToAsync(memoryStream);
imgfrmDB.Image_Byte = memoryStream.ToArray();
}
imgfrmDB.ImagePath = cloudDomaim + "/Images/" + imageFile.FileName;
imgfrmDB.Modify_By = "CMS Admin";
imgfrmDB.Modity_dt = DateTime.Now;
}
}
abUC.Title = title;
abUC.Context = context;
abUC.SubContext = subcontext;
abUC.Modify_By = "CMS Admin";
abUC.Modity_dt = DateTime.Now;
_context.Entry(abUC).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!AboutUsContextModelExists(abUC.AboutUs_Context_Id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok("About Us Content Delivered, Edit Successful");
}
Unfortunately when I run the code, the following errors only shows:
TypeError: Cannot read properties of null (reading 'listen')
at byte_stream.ByteStream.new.listen (http://localhost:60452/dart_sdk.js:36349:31)
at _StreamControllerAddStreamState.new._AddStreamState.new (http://localhost:60452/dart_sdk.js:37160:37)
at new _StreamControllerAddStreamState.new (http://localhost:60452/dart_sdk.js:37191:53)
at _AsyncStreamController.new.addStream (http://localhost:60452/dart_sdk.js:36687:24)
at _AsyncStarImpl.new.addStream (http://localhost:60452/dart_sdk.js:33462:46)
at multipart_request.MultipartRequest.new._finalize (http://localhost:60452/packages/http/src/multipart_request.dart.lib.js:352:22)
at _finalize.next (<anonymous>)
at _AsyncStarImpl.new.runBody (http://localhost:60452/dart_sdk.js:33416:40)
at Object._microtaskLoop (http://localhost:60452/dart_sdk.js:40808:13)
at _startMicrotaskLoop (http://localhost:60452/dart_sdk.js:40814:13)
at http://localhost:60452/dart_sdk.js:36279:9
I used breakpoints to examine each of the lines to track where is the null (in the Flutter's updateAboutUsContent() Future), which this line
final abUC =
await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
headers: <String, String>{
"Content-Type": "multipart/form-data;charset=UTF-8",
},
body: jsonEncode(<String, dynamic>{
"title": title,
"context": context,
"subContext": subcontext,
// 'imageFile': imageId
}));
and this line,
final request = http.MultipartRequest(
"POST", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
The 'PlatformFile imageFile' receives the imageFile. It shows the filename, bytes,... all those in the VS Code 'Run and Debug'.
The 'PlatformFile imageFile' still gets the image file, but until this line
request.files.add(http.MultipartFile(
"imageFile",
imageId.readStream,
imageId.size,
filename: imageId.name.split("/").last);
it still execute this line but after that the aforementioned TypeError shows.
It seems that, MAYBE
http.MultipartFile(
"imageFile",
imageId.readStream,
imageId.size,
filename: imageId.name.split("/").last)
↑↑something here was wrong.↑↑
Referring to the first two codes I pasted at first, i.e. updateAboutUsContent(), and PutAboutUsContent() located in the Web API, Can I know
Where did I done wrong?
How can I correct my 'updateAboutUsContent()' method, so that it can connect and 'PUT' data to the 'PutAboutUsContent()' in the .Net Core web API?
How can I convert Flutter's 'PlatformFile' to ASP's 'IFormFile' so that it binds to the corresponding argument that accepts the imageFile?
I'm very new to Flutter web, ASP.NET Core 5.0 web API, and really don't have any idea/concepts about how to upload images to the ASP.NET from flutter web, so something in my updateAboutUsContent() in the Flutter may wrote wrong.
I have tested the PutAboutUsContent() situated in the Web API using Swagger UI, nothing there was wrong, and I was prohibited to change the codes there, I just can use it.
So, I plead. Is there someone could lend a hand, please?
UPDATE
Now the Future updateAboutUsContent() async is changed to this:
Future<AboutUsContextModel> putAboutUsContent(
String title,
String context,
String subcontext,
PlatformFile imageId,
) async {
final abUC =
await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
headers: <String, String>{
"Content-Type": "multipart/form-data;charset=UTF-8",
},
body: jsonEncode(<String, dynamic>{
"title": title,
"context": context,
"subContext": subcontext,
// 'imageFile': imageId
}));
final request = http.MultipartRequest(
"PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
var fileadded =
new http.MultipartFile("imageFile",
imageId.readStream,
imageId.size,
filename: imageId.name);
if (fileadded != null) {
request.headers
.addAll(<String, String>{"Content-Type": "multipart/form-data"});
request.files.add(fileadded);
var resp = await request.send();
String response = await resp.stream.bytesToString();
print("==================\nThis is the response:\n=================" +
response);
}
if (abUC.statusCode == 200) {
//If the server did return a 200 Created All green post
//then parse the JSON
return AboutUsContextModel.fromJson(jsonDecode(abUC.body));
} else if (abUC.statusCode == 400) {
throw Exception('Error code was 400. About Us Content Not Foun');
} else {
throw Exception('Nothing About Us Content created in Flutter');
}
}
And the Future method can properly go until to the if(statusCode ==??) there, however, it still returns 400.
Now, the console appears:
==================
This is the response:
=================Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 23502: null value in column "Title" of relation "AboutUs_Context" violates not-null constraint
at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Exception data:
Severity: ERROR
SqlState: 23502
MessageText: null value in column "Title" of relation "AboutUs_Context" violates not-null constraint
Detail: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
SchemaName: WebContext
TableName: AboutUs_Context
ColumnName: Title
File: d:\pginstaller_13.auto\postgres.windows-x64\src\backend\executor\execmain.c
Line: 1953
Routine: ExecConstraints
--- End of inner exception stack trace ---
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Privacy_Web.Controllers.AboutUsContextModelsController.PutAboutUsContent(Int32 id, String title, String context, String subcontext, IFormFile imageFile) in D:\distributor_dashboard_v2\Privacy_Web\privacy_web_backend\Privacy_Web\Controllers\AboutUsContextModelsController.cs:line 224
at lambda_method299(Closure , Object )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Length: 263429
Content-Type: multipart/form-data; boundary=dart-http-boundary-zMVNRV2ehRqP4TYkdPpFn.dOrsckK2tfoxBV_s6z5coua9ye0+m
Host: localhost:44395
Referer: http://localhost:63925/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
origin: http://localhost:63925
sec-fetch-site: cross-site
sec-fetch-mode: cors
sec-fetch-dest: empty
x-server-sni: haproxy.privacy-web-kb5.conveyor.cloud
x-forwarded-for: (***IP address here, censored to be seen***)
When the first two lines of the updateAboutUsContent() method was executed, ie.
final abUC =
await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
headers: <String, String>{
"Content-Type": "multipart/form-data;charset=UTF-8",
},
body: jsonEncode(<String, dynamic>{
"title": title,
"context": context,
"subContext": subcontext,
// 'imageFile': imageId
}));
final request = http.MultipartRequest(
"PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
The following shows:
where the readStream returns: Instance of '_ControllerStream<List>'.
So I think this was the reason statusCode 400 was returned.
So, how should I solve it? Or if I wrongly identified the issue causing the error, then where can I improved?

You have to send one request with all data.
Something like this:
Future<AboutUsContextModel> putAboutUsContent(
String title,
String context,
String subcontext,
PlatformFile imageId,
) async {
var request = http.MultipartRequest("PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
request.fields['title'] = title;
request.fields['context'] = context;
request.fields['subContext'] = subContext;
request.files.add(http.MultipartFile(
'imageFile',
imageId.readStream,
imageId.size,
filename: imageId.name,
));
final resp = await request.send();
if (resp.statusCode == 200) {
//If the server did return a 200 Created All green post
//then parse the JSON
return AboutUsContextModel.fromJson(jsonDecode(resp.body));
} else if (resp.statusCode == 400) {
throw Exception('Error code was 400. About Us Content Not Foun');
} else {
throw Exception('Nothing About Us Content created in Flutter');
}
}
Also, check this answer .

Related

blazor wasm using Cryptography.Algorithms Error

Unhandled exception rendering component: System.Security.Cryptography.Algorithms is not supported on this platform.
System.PlatformNotSupportedException: System.Security.Cryptography.Algorithms is not supported on this platform.
at System.Security.Cryptography.Aes.Create()
at AutoTradingWebAppV2.Helper.Crypto.Encryptstring(String text, String keyString) in D:\Web\AutoTradingApp-BWASM\AutoTradingWebAppV2\Helper\Crypto.cs:line 12
at AutoTradingWebAppV2.Handler.CustomUpbitAuthHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\Web\AutoTradingApp-BWASM\AutoTradingWebAppV2\Handler\CustomUpbitAuthHandler.cs:line 31
at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<>n__0(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1[[System.Collections.Generic.IEnumerable`1[[DTOs.AccountDTO, DTOs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
at Services.UpbitService.GetAccounts() in D:\Web\AutoTradingApp-BWASM\Services\UpbitService.cs:line 31
at AppViewModels.UpbitTradingViewModel.GetAccounts() in D:\Web\AutoTradingApp-BWASM\AppViewModels\UpbitTradingViewModel.cs:line 156
at AutoTradingWebAppV2.Pages.TradingInfoBoard.TryConnectToWebsocket() in D:\Web\AutoTradingApp-BWASM\AutoTradingWebAppV2\Pages\TradingInfoBoard.razor.cs:line 48
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
https://learn.microsoft.com/en-us/dotnet/core/compatibility/cryptography/5.0/cryptography-apis-not-supported-on-blazor-webassembly
then how to create JWT at blazor ? or use AES?
how to fix it or when they update blazor?
var jwtToken = JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(SecretKey)
.AddClaim("access_key",AccessKey)
.AddClaim("nonce", Guid.NewGuid().ToString())
.Encode();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
I Added this code to httpclient handler, but can not use this code on blazor...
+
here is the open API sampleCode. I have to create JWT Token at Client and send request to api
C# CODE.
public class OpenAPISample {
public static void Main() {
var payload = new JwtPayload
{
{ "access_key", "Access Key" },
{ "nonce", Guid.NewGuid().ToString() },
{ "query_hash", queryHash },
{ "query_hash_alg", "SHA512" }
};
byte[] keyBytes = Encoding.Default.GetBytes("Secret Key");
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyBytes);
var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "HS256");
var header = new JwtHeader(credentials);
var secToken = new JwtSecurityToken(header, payload);
var jwtToken = new JwtSecurityTokenHandler().WriteToken(secToken);
var authorizationToken = "Bearer " + jwtToken;
}
}
JAVA Example CODE (there is no C# CODE in web site)
public static void main(String[] args) {
String accessKey = System.getenv("OPEN_API_ACCESS_KEY");
String secretKey = System.getenv("OPEN_API_SECRET_KEY");
String serverUrl = System.getenv("OPEN_API_SERVER_URL");
Algorithm algorithm = Algorithm.HMAC256(secretKey);
String jwtToken = JWT.create()
.withClaim("access_key", accessKey)
.withClaim("nonce", UUID.randomUUID().toString())
.sign(algorithm);
String authenticationToken = "Bearer " + jwtToken;
try {
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(serverUrl + "/v1/accounts");
request.setHeader("Content-Type", "application/json");
request.addHeader("Authorization", authenticationToken);
HttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity, "UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
how to create JWT at blazor ?
A JWT is created on the Server. And consumed by a Client app.
The APIs are unsupported for technical reasons but you shouldn't want to use them on a Client. Your client is not able to hide any credentials, it would only give false security.
That holds for AES as well. You can't hide the key.
they only give me accesskey and secret key and ask me make jwt
You should do this on your own Server. Your SPA client calls your Server that calls the Open API server.
Make sure the secret key never ends up in the SPA client.

HttpClient.SendAsync in DotNetCore - Is a Deadlock Possible?

We get an occasional TaskCanceledException on a call that, according to our logs, completes well inside the timeout we configure for the request. The first log entry is from the server. This is the last thing the method logs out before returning a JsonResult (MVC 4 Controller).
{
"TimeGenerated": "2021-03-19T12:08:48.882Z",
"CorrelationId": "b1568096-fdbd-46a7-8b69-58d0b33f458c",
"date_s": "2021-03-19",
"time_s": "07:08:37.9582",
"callsite_s": "...ImportDatasets",
"stacktrace_s": "",
"Level": "INFO",
"class_s": "...ReportConfigController",
"Message": "Some uninteresting message",
"exception_s": ""
}
In this case, the request took about 5 minutes to complete. Then 30 minutes later, our caller throws the task canceled exception from HttpClient.SendAsync:
{
"TimeGenerated": "2021-03-19T12:48:27.783Z",
"CorrelationId": "b1568096-fdbd-46a7-8b69-58d0b33f458c",
"date_s": "2021-03-19",
"time_s": "12:48:17.5354",
"callsite_s": "...AuthorizedApiAccessor+<CallApi>d__29.MoveNext",
"stacktrace_s": "TaskCanceledException
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)\r\n
at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)\r\n
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)\r\n
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n
at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)\r\n
at ...AuthorizedApiAccessor.CallApi(String url, Object content, HttpMethod httpMethod, AuthenticationType authType, Boolean isCompressed)\r\nIOException
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)\r\n
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)\r\n
at System.Net.Security.SslStream.<FillBufferAsync>g__InternalFillBufferAsync|215_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial)\r\n
at System.Net.Security.SslStream.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer)\r\n
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)\r\nSocketException",
"Level": "ERROR",
"class_s": "...AuthorizedApiAccessor",
"Message": "Nothing good",
"exception_s": "The operation was canceled."
}
Given that within the process that makes the request we block an async call (.Result -- hitting a brownfield caching implementation that doesn't support async), my first guess was that we had a deadlock as described by Stephen Cleary. But the caller is a dotnetcore 3.1 application, so that kind of deadlock is not possible.
Our usage of HttpClient is pretty standard, I think. This is the method that ultimately makes the call:
private async Task<string> CallApi(string url, object content, HttpMethod httpMethod, AuthenticationType authType, bool isCompressed)
{
try
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = httpMethod,
Content = GetContent(content, isCompressed)
};
AddRequestHeaders(request);
var httpClient = _httpClientFactory.CreateClient(HTTPCLIENT_NAME);
httpClient.Timeout = Timeout;
AddAuthenticationHeaders(httpClient, authType);
var resp = await httpClient.SendAsync(request);
var responseString = await (resp.Content?.ReadAsStringAsync() ?? Task.FromResult<string>(string.Empty));
if (!resp.IsSuccessStatusCode)
{
var message = $"{url}: {httpMethod}: {authType}: {isCompressed}: {responseString}";
if (resp.StatusCode == HttpStatusCode.Forbidden || resp.StatusCode == HttpStatusCode.Unauthorized)
{
throw new CustomException(message, ErrorType.AccessViolation);
}
if (resp.StatusCode == HttpStatusCode.NotFound)
{
throw new CustomException(message, ErrorType.NotFound);
}
throw new CustomException(message);
}
return responseString;
}
catch (CustomException) { throw; }
catch (Exception ex)
{
var message = "{Url}: {HttpVerb}: {AuthType}: {IsCompressed}: {Message}";
_logger.ErrorFormat(message, ex, url, httpMethod, authType, isCompressed, ex.Message);
throw;
}
}
We are at a loss for theories on this behavior. We've seen the task cancelation between 3-5 times a month within a few hundred successful requests, so it's intermittent, but far from rare.
Where else should we be looking for the source of what behaves like a deadlock?
update
Might be relevant to note that we are using the standard HttpClientHandler. The retry policies have been added recently, but we don't retry on a long-running POST, which is the scenario above.
builder.Services.AddHttpClient(AuthorizedApiAccessor.HTTPCLIENT_NAME)
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
{
AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip
})
.AddRetryPolicies(retryOptions);

IFeatureCollection is disposed Object name: Collection in a Task?

I want to run a Task to save the logs into a database table.
But it seems like that the HttpContext is disposedin the other thread.
Here is my code:
_ = Task.Run(() =>
{
using (var db = LogContext())
{
var request = new LoginLog
{
Url = context.HttpContext.Request.Path,
UserAgent = context.HttpContext?.Request?.Headers?[HeaderNames.UserAgent].ToString()
};
await db.AddAsync(request);
db.SaveChanges();
}
});
Here is the exception trackInfo:
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature, TState](TFeature & cached, TState state, Func`2 factory)
at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Path()
at PowerMan.Applet.Api.Infrastructure.Filters.RequestFilter.<> c__DisplayClass4_0.< OnActionExecutionAsync > b__1() in / Users / zhaojing / Projects / PowerMan.Api / PowerMan / PowerMan.Applet / PowerMan.Applet.Api / Infrastructure / Filters / RequestFilter.cs:line 112
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.<> c.<.cctor > b__274_0(Object obj)
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
Is it the Task which make the exceptions?
Most definitely the context is disposed after the request completes.
So you will need to capture the values you will need before starting the background operation:
var url = context.HttpContext.Request.Path;
var userAgent = context.HttpContext?.Request?.Headers?[HeaderNames.UserAgent].ToString();
_ = Task.Run(() =>
{
using (var db =LogContext())
{
var request = new LoginLog
{
Url = url,
UserAgent = userAgent
};
await db.AddAsync(request);
db.SaveChanges();
}
});

How to get a user from an IGraphServiceClient object?

I have a POST method and want to get a user from an IGraphServiceClient object (in the Graph.Microsoft package and namespace).
The GET method works fine. Then I take a user from this list and set them as a parameter for my POST method.
public async Task<Dictionary<string, List<string>>> GetUserGroupsAsync(ICollection<string> userIds)
{
var aggregatedUserGroupMap = new Dictionary<string, List<string>>();
foreach (string userId in userIds)
{
try
{
var userMemberOfCollectionRequest = graphServiceClient.Users[userId].MemberOf.Request();
var userMemberOfCollection = await userMemberOfCollectionRequest.GetAsync().ConfigureAwait(false);
if (!aggregatedUserGroupMap.ContainsKey(userId)) { aggregatedUserGroupMap.Add(userId, new List<string>()); }
foreach (var memberOf in userMemberOfCollection) { aggregatedUserGroupMap[userId].Add(memberOf.Id); }
}
catch (Exception ex)
{
throw ex;
}
}
return aggregatedUserGroupMap;
}
The values in the incoming string collection, userIds, are user email addresses, copied from the GET result.
The value of userMemberOfCollectionRequest looks fine. The RequestUrl property contains "https://graph.microsoft.com:443/v1.0/users/my-email#compagny.com/memberOf". Headers and QueryOptions are empty collections.
In the above method, the following line throws an exception:
var userMemberOfCollection = await userMemberOfCollectionRequest.GetAsync().ConfigureAwait(false);
The exception message reads:
Request_ResourceNotFound
Resource 'my-email#compagny.com' does not exist or one of its queried reference-property objects are not present.
at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at Microsoft.Graph.BaseRequest.SendRequestAsync(Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
at Microsoft.Graph.BaseRequest.SendAsync[T](Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
at Microsoft.Graph.UserMemberOfCollectionWithReferencesRequest.GetAsync(CancellationToken cancellationToken)
at xxx.xxx.BusinessComponent.GraphBC.GetUserGroupsAsync(ICollection`1 userIds) in C:\workspace\xxx\xxx.xxx\xxx.xxx.Core\BusinessComponent\GraphBC.cs:line 50
Does anyone have an idea for where I should look to solve this problem?
The solution was not to use the email, instead using the ObjectId property (a GUID instead of an email).

Unity app not responding and get "request time out" when call to WebAPI

I'm having a Unity app call to a Like() method in DLL, which connect to a WebAPI to do the job. However, when I debug the application and code run to
string liked = SocialConnector.LikePost(token, postID);
It got stuck there, unity became not responding and after work normal again, the debug show Request time out.
Below are my detail code in DLL
public static String LikePost(String token, String postID)
{
HttpCommon common = new HttpCommon();
string url = Constant.ApiURL + Constant.API_LINK_POST_LIKE_UNLIKE + "?postID=" + postID;
String result = common.HttpPost<String>(url, token);
return result;
}
Code in HTTPCommon
public T HttpPost<T>(String url, String token)
{
var request = (HttpWebRequest)WebRequest.Create(url);
//var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
//request.ContentLength = data.Length;
request.Headers["Authorization"] = "Bearer " + token;
var stream = request.GetRequestStream();
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
try
{
var responseData = JsonConvert.DeserializeObject<T>(responseString);
return responseData;
}
catch (Exception e)
{
throw new Exception(responseString);
}
}
Error log:
WebException: The request timed out
System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
System.Net.HttpWebRequest.GetResponse ()
GSEP_DLL.Connectors.HttpCommon.HttpPost[String] (System.String url, System.String token)
GSEP_DLL.Connectors.SocialConnector.LikePost (System.String token, System.String postID)
Like.onClick () (at Assets/Scripts/Like.cs:39)
UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:110)
UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:574)
UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:716)
UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:53)
UnityEngine.UI.Button.Press () (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:35)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:44)
UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52)
UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:269)
UnityEngine.EventSystems.EventSystem:Update()
Update: Problem solved. The WebAPI return type of bool but my DLL forced to return String.
Problem solved. The WebAPI return type of bool but my DLL forced to return String.