I'm trying to make my own custom System.Web.Http.AuthorizeAttribute. Everything was working until I needed to run some async function inside of the overridden OnAuthorizationfunction.
I found the OnAuthorizationAsync method and I use that, however still having issues running an async method. I need to ensure that a header attached to the HttpActionContext is in the db.
public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues("Authentication", out values))
{
var token = new Token() { TokenString = values.First() };
if (await _tg.ValidateToken(token))
{
return base.OnAuthorizationAsync(actionContext, cancellationToken);
}
}
base.HandleUnauthorizedRequest(actionContext);
}
_tg.ValidateToken(token) is an async method that returns a bool. If I try to await it and make OnAuthorizationAsync an async method I then apparently cant return a Task:
Since OnAuthorizeAsync is an async method that returns 'Task', a return keyword must not be followed by an object expression.
Is there a way you can see out of this mess?
Because you use await, you need to add the async modifier to the method's declaration. Then change the return you have there to await. The compiler will do the rest - i.e. returning the Task for you.
Based on #sellotape's answer:
public async override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues("Authentication", out values))
{
var token = new Token() { TokenString = values.First() };
if (await _tg.ValidateToken(token))
{
return base.OnAuthorizationAsync(actionContext, cancellationToken);
}
}
await base.HandleUnauthorizedRequest(actionContext);
}
Related
I have a generic method that does a Post:
protected async Task<T> PostAsync<T>(string resource, object value = null)
{
T result = default(T);
var client = _httpClientFactory.CreateClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Post, _baseUri + resource);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
if (value != null)
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json");
using (var response = await client.SendAsync(requestMessage))
{
try
{
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(responseContent);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
//if (!response.IsSuccessStatusCode)
//throw new HttpStatusCodeException((int)response.StatusCode, await response.Content.ReadAsStringAsync());
}
}
return result;
}
The API controller method that I am calling looks like this (I have omitted some implementation details). As you can see, it doesn't return any data:
[HttpPost("{contactId}/opt-out/test")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateTestAsync(Guid contactId, [FromQuery] string source)
{
try
{
return Ok();
}
catch (Exception exception)
{
}
}
Now using the generic Post method, I would like to know how to call the API endpoint. I have tried the following:
public async Task UpdateContactOptOutAsync(Guid contactId, string source)
{
return await base.PostAsync<Task>($"contacts/{contactId}/opt-out/test?source={source}");
}
But I get the following error:
Since 'BeamApiRepository.UpdateContactOptOutAsync(Guid, string)' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
So how do I specify a return type to call an API endpoint that doesn't return any value?
it is better not to use it without T since it used to deserialize result. So I recomend to use it this way (PostAsync< Task > will work too)
public async Task UpdateContactOptOutAsync(Guid contactId, string source)
{
await base.PostAsync<Task<object>>($"contacts/{contactId}/opt-out/test?source={source}");
}
if your API dosn't return any data it will return null, otherwise it will still return the result as an object instance. But the code ignores it since you have void method. You can use var result = await base.Post.... and put some error code if it is not null, but it is out of the scope of this question.
Your Http client will likely not return a Task<Task> so why are you calling PostAsync<Task>(...)?
If you are using C# 7 or lower, you can simply call PostAsync<object>(...) which will always return null for the API you are calling.
If you are using C# 8 or higher, you can call PostAsync<object?>(...).
Better yet, you can add a generic method which simply returns Task (with no return type), so it for API POST calls with no response body. That way, it is clear that the response has nothing.
protected async Task PostAsync(string resource, object value = null)
{
....
}
Assuming you have your own implementation of Task (and the generic type in the call to PostAsync<Task> is not a mistake) then you need UpdateContactOptOutAsync to return System.Threading.Tasks.Task<Task>.
(just make sure to not mix-up the namespaces)
public async Task<Task> UpdateContactOptOutAsync(Guid contactId, string source)
Otherwise, if you haven't implemented your own Task class, then UpdateContactOptOutAsync should look something like this:
public async Task<MyResponse> UpdateContactOptOutAsync(Guid contactId, string source)
{
return await base.PostAsync<MyResponse>($"contacts/{contactId}/opt-out/test?source={source}");
}
Where MyResponse is the actual type you expect to deserialize from the response.
You may need to specify the return Task type, like this:
public async Task<IActionResult> UpdateContactOptOutAsync(Guid contactId, string source)
{
return await base.PostAsync<Task>($"contacts/{contactId}/opt-out/test?source={source}");
}
Please help me to understand why this code cause a deadlock?
I have an asp.net web api application and I tried to make some controller method asynchronous.
[HttpPost]
[Authentication]
public async Task<SomeDTO> PostSomething([FromBody] SomeDTO someDTO)
{
return await _service.DoSomething(someDTO);
}
this is how looks the called service method:
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
...
var someTask = Task.Run(() =>
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
...
var result = await someTask;
...
}
And there is some globalhandler, that prints a response to a console.
public class AppGlobalHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = base.SendAsync(request, cancellationToken);
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{resp?.ConfigureAwait(false).GetAwaiter().GetResult()?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult()}");
return resp;
}
}
Looks like ConfigureAwait(false).GetAwaiter().GetResult()
blocks the caller thread, but I supposed that ConfigureAwait(false) should avoid this, isn't it?
ConfigureAwait(false) would not help you here because it must be all the way down in the call stack (see more here) not at place where you wait synchronously, i.e. it depends rather on the implementation of base.SendAsync. If it acquired a lock on current thread it's too late to do something about it. It is also not recommended in ASP.net pipeline to continue responding on other thread after all (see discussion here and post here).
Finally it is always a highly risky idea to wait synchronously in async context.
If you need to read content, why not doing it like that:
public class AppGlobalHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = await base.SendAsync(request, cancellationToken);
var content = resp?.Content != null
? (await resp.Content.ReadAsStringAsync())
: string.Empty;
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{content}");
return resp;
}
}
I think you overlook async keyword in Task.Run() method.
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
var someTask = Task.Run( async () => //simply add this for async run
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
var result = await someTask;
}
In the SignalR hub I've this:
public class MyHub : Hub
{
public override Task OnConnected()
{
// my async code here
return base.OnConnected();
}
}
I want to perform an async code. So I added async keyword like this:
public class MyHub : Hub
{
public override async Task OnConnected()
{
var result = await MyAsyncMethod();
return base.OnConnected();
}
}
but return base.OnConnected(); shows this error:
Since MyHub.OnConnected() is an async method that returns Task, a
returned keyword must not be followed by an object expression. Did you
intend to return Task<T>?
How can I fix it? thanks.
An async method is converted into a state machine by the compiler. You cannot return that Task here, because the Task returned is generated by the compiler and represents the continuation of this method.
Simply await the base call:
public override async Task OnConnected()
{
var result = await MyAsyncMethod();
await base.OnConnected();
}
I try to create a filter to modify the content. For some reason the
var result = await actionContext.Request.Content.ReadAsStringAsync(); does not wait and returns me empty values. I'm sure that there is data. Checked directly inside controller and header. Is there maybe a workaround. Can be blocking, too (HttpContent seems just to have async methods).
public class AsyncAttribute : FilterAttribute, IActionFilter
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
await InternalActionExecuting(actionContext, cancellationToken);
if (actionContext.Response != null)
{
return actionContext.Response;
}
HttpActionExecutedContext executedContext;
try
{
var response = await continuation();
executedContext = new HttpActionExecutedContext(actionContext, null)
{
Response = response
};
}
catch (Exception exception)
{
executedContext = new HttpActionExecutedContext(actionContext, exception);
}
await InternalActionExecuted(executedContext, cancellationToken);
return executedContext.Response;
}
public virtual async Task InternalActionExecuting(HttpActionContext actionContext, CancellationToken cancellationToken)
{
//////////////////////////////////////////////////////////////////////////
var result = await actionContext.Request.Content.ReadAsStringAsync();// <------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
}
public virtual async Task InternalActionExecuted(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
}
}
You should not be accessing the body in an action filter, if you have any thing else accessing the body in parameter binding it might be already gone.
Why are you calling actionExecuted inside the action executing overload? The pattern is one gets called before the action is run and one after.
For some reason it also looks like you are trying to implement the filter pipeline itself inside this method.
If you want to bind data from the body your recommended alternative is to use a formatter. Implement a MediaTypeFormatter and read what you need into a string. See one example
My wild bet here is that something else already read the body stream (and technically you are done at the point) and you can try and rewind it first (it will only work if you are in buffered mode).
i am creating a task scheduler so i am trying to make some kind of repeating function that accepts Task and awaits it but i get a strange Exception of Type 'T' is not awaitable
public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token)
{
return Task.Factory.StartNew(
async () =>
{
for (; ; )
{
if (token.WaitCancellationRequested(pollInterval))
break;
await action();
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
So can anyone tell me how could i await a that generic Task cuz i want the function to accept any Task, Task, bool or any other type ?
You don't need to start a long running task for this - just make your method asynchronous directly:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(true)
{
await Task.Delay(pollInterval, token);
action();
}
}
This will cause the Action to run on the current context. If that is not required, you can use:
await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();
This will cause the Action to not run on the same synchronization context of the caller, and potentially use a ThreadPool thread.
Edit in response to comments:
If you don't want the resulting task to come back canceled, but just return when the token is fired, you could use:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
action();
}
catch(OperationCanceledException e)
{
// Swallow cancellation - dangerous if action() throws this, though....
break;
}
}
}
Edit 2:
If you want to pass in async lambdas, you should make the method take an Func<Task>, not Action:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Func<Task> actionTask, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
await actionTask();
}
}
Edit in response to chat:
If you want to poll, but use the results of an operation, you could use:
public static async Task RunAtIntervalAsync<T>(TimeSpan pollInterval, Func<Task<T>> fetchOperation, Action<T> operationOnResult, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
// Get a value
T value = await fetchOperation();
// Use result (ie: update UI)
operationOnResult(value);
}
}
You could then call this via:
RunAtIntervalAsync(TimeSpan.FromSeconds(1),
async () => { await Task.Delay(1000); return "Foo"; },
result => UpdateUI(result),
token);
You can't.
You can make a function that takes a generic asynchronous function – a function that returns a Task<T>.
That would be a Func<Task<T>>.
You can also make a function that takes a generic synchronous function, which is what you have now.
You can't make a single function that can take either, but you can make two overloads.
On an unrelated note, your function never actually uses the return value of the function.
Therefore, you shouldn't make it generic at all; you should instead take a Func<Task> or an Action.
Check an example here post.
Agree with SLaks, you need to make the generic parameter T of Func awaitable in order to use the await.
For example if T is a string the code would "await" for a function that returns just a string.
The await is valid only for Tasks. For more info check this explanation MSDN Blog; the example is in VB.net.