How to get a user from an IGraphServiceClient object? - asp.net-core

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).

Related

Flutter web: How to upload Image to ASP .NET Core web API?

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 .

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);

Error in WCF service value does not fall within the expected range

In one of our code, we are getting below error.
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Web.Util.Misc.ThrowIfFailedHr(Int32 hresult)
at System.Web.Hosting.IIS7WorkerRequest.SetUnknownRequestHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.SetHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.Add(String name, String value)
Code is as below:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string correlationId = newAuditTrail.GetCorrelationIdFromRequest(request).ToString();
string url = newAuditTrail.GetUrlFromRequest(request).ToString();
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
HttpContext.Current.Request.Headers.Add("Url", url);
Error is thrown on line:
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
I noticed operation contract of method, it is defined as oneway.
[OperationContract(IsOneWay=true)]
If you want to add http header in the http request, please refer to the below code segements.
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
{
httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
We could also use WebOperationContext to add http header on the client-side.
Add Request header to WCF when using ConfigurationChannelFactory.CreateChannel
Please refer to the below discussion.
How to add a custom HTTP header to every WCF call?
Feel free to let me know if the problem still exists.

Forward POST request from asp.net core controller to different URL

I wanto to forward an incoming POST request to my asp.net core controller "as is" (including headers, body, from-data) to a different URL without using a middleware.
I found an example for doing that for asp.net: https://philsversion.com/2012/09/06/creating-a-proxy-with-apicontroller/
But this does not work for asp.net core, since the call to
return await http.SendAsync(this.Request);
in asp.net core accepts an HttpRequestMessage and the Request object is of type HttpRequest.
I also found some code, which creates a HttpRequestMessage from an HttpRequest, see: Convert Microsoft.AspNetCore.Http.HttpRequest to HttpRequestMessage
Using this code, the receiving endpoint (to which I forward to) gets the Body, but it does not get Form fields.
Checking the class HttpRequestMessage I saw that it does not contain a property for FormFields.
[Microsoft.AspNetCore.Mvc.HttpPost]
[NrgsRoute("api/redirect-v1/{key}")]
public async Task<HttpResponseMessage> Forward(
[FromUri] string key,
CancellationToken cancellationToken)
{
// the URL was shortened, we need to get the original URL to which we want to forward the POST request
var url = await _shortenUrlService.GetUrlFromToken(key, cancellationToken).ConfigureAwait(false);
using (var httpClient = new HttpClient())
{
var forwardUrl = new Uri(url);
Request.Path = new PathString(forwardUrl.PathAndQuery);
// see: https://stackoverflow.com/questions/45759417/convert-microsoft-aspnetcore-http-httprequest-to-httprequestmessage
var requestMessage = Request.ToHttpRequestMessage();
return await httpClient.SendAsync(requestMessage, cancellationToken);
// Problem: Forwards header and body but NOT form fields
}
}
Expected result would be that at my receiving endpoint I have the same
- headers
- body
- form fields
as in the original POST request.
I ended up doing the following:
[HttpPost]
[NrgsRoute("api/redirect-v1/{key}")]
public async Task<RedirectResult> Forward(string key, CancellationToken cancellationToken)
{
var url = await _shortenUrlService.GetUrlFromToken(key, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(url))
throw new BadRequestException($"Could not create forward URL from parameter {key}", "redirect-error");
using (var httpClient = new HttpClient())
{
var forwardUrl = new Uri(url);
Request.Path = new PathString(forwardUrl.PathAndQuery);
HttpResponseMessage responseMessage;
if (Request.HasFormContentType)
responseMessage = await ForwardFormData(key, httpClient, forwardUrl, cancellationToken);
else
responseMessage = await ForwardBody(key, httpClient, cancellationToken);
var queryParams = forwardUrl.GetQueryStringParams();
var lUlr = queryParams["lurl"];
return new RedirectResult(lUlr);
}
}
private async Task<HttpResponseMessage> ForwardFormData(string key, HttpClient httpClient, Uri forwardUrl, CancellationToken cancellationToken)
{
var formContent = new List<KeyValuePair<string, string>>();
HttpResponseMessage result;
if (Request.ContentType == "application/x-www-form-urlencoded")
{
foreach (var formKey in Request.Form.Keys)
{
var content = Request.Form[formKey].FirstOrDefault();
if (content != null)
formContent.Add(new KeyValuePair<string, string>(formKey, content));
}
var formUrlEncodedContent = new FormUrlEncodedContent(formContent);
result = await httpClient.PostAsync(forwardUrl, formUrlEncodedContent, cancellationToken);
}
else
{
var multipartFormDataContent = new MultipartFormDataContent();
foreach (var formKey in Request.Form.Keys)
{
var content = Request.Form[formKey].FirstOrDefault();
if (content != null)
multipartFormDataContent.Add(new StringContent(content), formKey);
}
result = await httpClient.PostAsync(forwardUrl, multipartFormDataContent, cancellationToken);
}
return result;
}
private async Task<HttpResponseMessage> ForwardBody(string key, HttpClient httpClient, CancellationToken cancellationToken)
{
// we do not have direct access to Content, see: https://stackoverflow.com/questions/41508664/net-core-forward-a-local-api-form-data-post-request-to-remote-api
var requestMessage = Request.ToHttpRequestMessage();
return await httpClient.SendAsync(requestMessage, cancellationToken);
}

WebApi 2: Custom HttpResponseMessage after handling OperationCanceledException in Message Handler is not returned to client

I am currently investigating in WebApi 2 Message Handler and how to implement a server side timeout using cancellation tokens. If a cancellation occurs a OperationCanceledException is thrown and handled in my Message Handler. In this case I return a HttpResponseMessage with a adequate HttpStatusCode (HttpStatusCode.RequestTimeout).
I expected that my consuming client (using postman) retrieves this HttpStatusCode, but instead "Could not get any response" is displayed, thus my client aborts without any additional information. Could someone explain to me whats the deal with this behavior? What am I missing?
See following example code:
public class RequestTimeoutHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeSpan timeout = TimeSpan.FromSeconds(10.0);
using (CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(timeout))
using (CancellationTokenSource linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellationTokenSource.Token))
{
try
{
return await base.SendAsync(request, linkedCancellationTokenSource.Token);
}
catch (OperationCanceledException e)
{
return new HttpResponseMessage(HttpStatusCode.RequestTimeout);
}
}
return null;
}
}
My Test Controller Method looks as follows:
[Route("testTimeoutAsyncHandleException"), HttpPost]
public IHttpActionResult TestTimeoutAsynchandle(string hugo, CancellationToken ct)
{
for (int i = 0; i < 500; i++)
{
Thread.Sleep(1000); //sleep 1 sec until exception is thown
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
}
return Ok("yes");
}
turns out, using postman was not my best idea. I created a console application using a httpclient and requested my test method. the httpstatus 408 is returned as expected.