How can I successfully use HttpClient to access iTunes search service in MVC4 app - asp.net-mvc-4

I am writing an MVC4 app, and in the controller, I need to be able to make a call to a web service to access the iTunes search service in order to get some cover art for a DVD.
I have the following very simple code
public static string GetAlbumArt(Movie movie) {
Task<string> lookupTask = LookupAlbumArt(movie.Title, movie.Year);
lookupTask.Wait(5000);
if (lookupTask.IsCompleted)
{
return lookupTask.Result;
}
else
{
return Timeout;
}
}
private static async Task<string> LookupAlbumArt(string title, int year)
{
using (var client = new HttpClient())
{
string response= await client.GetStringAsync(
"http://itunes.apple.com/search?term=hoosiers&media=movie"
);
return response;
}
}
When I run this and set breakpoints at the return Timeout line in GetAlbumArt and also at the return response line in LookupAlbumArt, the Timeout breakpoint is hit first, and then the LookupAlbumArt return statement is hit, and the correct content is in the response variable.
If I remove the timeout parameter from lookupTask.Wait(5000), the wait GetStringAsync call never returns, and the page never loads.
This feels like a pretty straightforward task, and I'm stumped as to what I'm doing wrong.

Related

Nothing happens when Redirect(url) is executed

I'm trying to call the async method before return View() so that the heavy method request does not interfere with the appearance of the View, but works a little later (so that inscriptions appear on the screen that the process needs 1-3 seconds to execute). Everything is fine, but when the asynchronous method performs its task, I call Redirect(url) in it, but it does not work - just silence, no errors, silence like in a crypt.
The browser does not redirect me to another https page. Why? This is a simple action and I need it, because I get a certain protected token-link to a protected token-page from the outside in an asynchronous method - and there is nothing terrible about it.
How do I solve this problem?
Thanks!
public async Task<IActionResult> Method(Params params)
{
AsyncDo(params);
return View(params); // The View is shown! Good!
}
private async Task<IActionResult> AsyncDo(Params params)
{
ResultAsync resultAsync = await Task.Run(() =>
{
return WebAPI.LongHardWebRequest(params);
});
// This is triggered after 1-3 seconds
return Redirect(resultAsync.url); // It doesn't work!
HttpContext.Response.Redirect(resultAsync.url); // It doesn't work!
return RedirectToAction("RedirectTo", resultAsync.url); // It doesn't work!
return View();
}
// The method is never called!
public IActionResult RedirectTo(string url)
{
return Redirect(url);
return View();
}
I tried to save return values in static - it doesn't help. I tried calling the Redirect method without return. - it doesn't help. Nothing helps! Why? It's a simple action.I tried return Redirect(url), HttpContext.Response.Redirect(url), RedirectToAction("Action", url) - nothing helps - nothing happens.

Use cancellation token to cancel execution of multiple tasks executed using Task.WhenAll<tasks> method in asp.net core 3.1

I am working on an asp.net core application which has downloading files functionality. Using this function we can create multiple download tasks and execute them at once using Task.Whenall.
I am trying to implement cancel download functionality using which we can abort/cancel the complete download operation by clicking a cancel button on one of my razor pages (if I selected 100 files to download and clicked cancel after downloading 10 files, 90 remaining files should not be downloaded).
Currently it cannot be cancelled, once the download operation is started it continuously executes in background even if we close the download page unless all files fail/succeed to download. Exiting the application stops it.
Implementation is as follows.
DownloadsController class:
//variable declaration
CancellationTokenSource cts;
[HttpPost]
public async Task<IActionResult> Download_iles(DownloadFilesModel downloadFilesModel)
{
cts=new CancellationTokenSource();
var successFiles = await _downloadManager.DownloadAsyncpa(downloadFilesModel.param1, downloadFilesModel.param2, cts.Token);
}
ManageFileDownlods class:
public class ManageFileDownlods : BackgroundService, IManageFileDownlods
{
//Constructor
public ManageFileDownlods(IDownloadmaster downloadmaster)
{
_downloadmaster = downloadmaster;
}
public async Task<List<DownloadFile>>
DownloadAsync (funcparam1,funcparam2,CancellationToken cancellationToken=default)
{
// For each server in serverQueue these multiple tasks will execure
while (serverQueue.Count > 0)
{
//Multiple tasks created to downloadfiles
downloadTasksList.Add(ServerProcess(funcparam1, funcparam2, cancellationToken));
//Multiple tasks executed here to download files
try
{
await Task.WhenAll(downloadTasksList.ToArray());
}
catch()
{ }
}
}
private async Task<List<DownloadFile>> ServerProcess (funcparam1, funcparam2,
CancellationToken cancellationToken)
{
while (funcparam1.Count > 0)
{
//5 taks created in loop
for (int i = 0; i < 5; i++)
{
//Multiple tasks created to downloadfiles
fileDownlodTasks.Add(_downloadmaster.Download(param1, param2,
cancellationToken));
await Task.Delay(300);
}
try
{
//Multiple tasks executed here to download files
await Task.WhenAll(fileDownlodTasks.ToArray());
}
catch (TaskCanceledException ex)
{
Debug.WriteLine("execution stopped");
throw ex;
}
}
}
}
Downloadmaster Class:
public async Task<DownloadFile> Download (param1,param2,CancellationToken cancellationToken)
{
//actual function which initiated file download from server
var filecontents = DownloadFileFromServer (param1,param2, cancellationToken);
}
I've spent much time on internet, gone through a lot of different articles over cancellation of tasks, tried to implement multiple approaches given in these articles, but unable to cancel the operation.
Probably, you should add CancellationToken to the action parameters. ASP.NET MVC and ASP.NET Core map that parameter to HttpContext.RequestAborted token. More details here

Using Attribute and ActionFilters for logging request and response of controller and actions

I am trying to find an elegant way of logging every request and response in my Web API using Filters in Asp.net Core 3.1 rather than have them in each action and each controller.
Haven't found a nice solution that seems performable well to deploy in production.
I've been trying to do something like this (below) but no much success.
Any other suggestion would be appreciated.
public class LogFilter : IAsyncActionFilter
{
private readonly ILogger _logger;
public LogFilter(ILogger logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
var responseBodyData = "";//how to get the response result
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
await next();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
}
I think logging the response should be done in debugging mode only and really can be done at your service API (by using DI interception). That way you don't need to use IActionFilter which actually can provide you only a wrapper IActionResult which wraps the raw value from the action method (which is usually the result returned from your service API). Note that at the phase of action execution (starting & ending can be intercepted by using IActionFilter or IAsyncActionFilter), the HttpContext.Response may have not been fully written (because there are next phases that may write more data to it). So you cannot read the full response there. But here I suppose you mean reading the action result (later I'll show you how to read the actual full response body in a correct phase). When it comes to IActionResult, you have various kinds of IActionResult including custom ones. So it's hard to have a general solution to read the raw wrapped data (which may not even be exposed in some custom implementations). That means you need to target some specific well-known action results to handle it correctly. Here I introduce code to read JsonResult as an example:
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
var actionExecutedContext = await next();
var responseBodyData = "not supported result";
//sample for JsonResult
if(actionExecutedContext.Result is JsonResult jsonResult){
responseBodyData = JsonSerializer.Serialize(jsonResult.Value);
}
//check for other kinds of IActionResult if any ...
//...
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
IActionResult has a method called ExecuteResultAsync which can trigger the next processing phase (result execution). That's when the action result is fully written to the HttpContext.Response. So you can try creating a dummy pipeline (starting with a dummy ActionContext) on which to execute the action result and get the final data written to the response body. However that's what I can imagine in theory. It would be very complicated to go that way. Instead you can just use a custom IResultFilter or IAsyncResultFilter to try getting the response body there. Now there is one issue, the default HttpContext.Response.Body is an HttpResponseStream which does not support reading & seeking at all (CanRead & CanSeek are false), we can only write to that kind of stream. So there is a hacky way to help us mock in a readable stream (such as MemoryStream) before running the code that executes the result. After that we swap out the readable stream and swap back the original HttpResponseStream in after copying data from the readable stream to that stream. Here is an extension method to help achieve that:
public static class ResponseBodyCloningHttpContextExtensions
{
public static async Task<Stream> CloneBodyAsync(this HttpContext context, Func<Task> writeBody)
{
var readableStream = new MemoryStream();
var originalBody = context.Response.Body;
context.Response.Body = readableStream;
try
{
await writeBody();
readableStream.Position = 0;
await readableStream.CopyToAsync(originalBody);
readableStream.Position = 0;
}
finally
{
context.Response.Body = originalBody;
}
return readableStream;
}
}
Now we can use that extension method in an IAsyncResultFilter like this:
//this logs the result only, to write the log entry for starting/beginning the action
//you can rely on the IAsyncActionFilter as how you use it.
public class LoggingAsyncResultFilterAttribute : Attribute, IAsyncResultFilter
{
//missing code to inject _logger here ...
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var readableStream = await context.HttpContext.CloneBodyAsync(() => next());
//suppose the response body contains text-based content
using (var sr = new StreamReader(readableStream))
{
var responseText = await sr.ReadToEndAsync();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseText}");
}
}
}
You can also use an IAsyncResourceFilter instead, which can capture result written by IExceptionFilter. Or maybe the best, use an IAsyncAlwaysRunResultFilter which can capture the result in all cases.
I assume that you know how to register IAsyncActionFilter so you should know how to register IAsyncResultFilter as well as other kinds of filter. It's just the same.
starting with dotnet 6 asp has HTTP logging built in. Microsoft has taken into account redacting information and other important concepts that need to be considered when logging requests.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
/* enabled HttpLogging with this line */
app.UseHttpLogging();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.Run();
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0#enabling-http-logging

How to fetch initial data using provider in flutter effectievly

Recently, i did a flutter course.
The instructor was making the get request from an API so difficult. For a hybrid framework like flutter i never thought it's so difficult.
below are my code. I am using provider for state management.
Future<void> fetchAndSetProducts() async {
try {
const url = 'fetch-url';
final response = await http.get(url);
final data = json.decode(response.body) as Map<String, dynamic>;
final List<Product> loadedProducts = [];
data.forEach((key, value) {
loadedProducts.add(Product(
id: key,
title: value['title'],
description: value['description'],
imageUrl: value['imageUrl'],
price: value['price'],
isFavorite: value['isFavorite'],
));
});
_items = loadedProducts;
notifyListeners();
} catch (error) {
throw (error);
}
}
And in the products overview screen were I am showing the products page this method is called like below:
bool _isInit = true;
bool _isLoading = false;
#override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<Products>(context).fetchAndSetProducts().then((_) => {
setState(() {
_isLoading = false;
})
});
}
_isInit = false;
super.didChangeDependencies();
}
The other method included a sneaky way of using duration of zero just like we use in javascript set timeout giving a zero time.
It's worth noting that in didChangeDependencies we could not use async await, so most probably a call back hell awaits.
Also a variable needs to be initialized just for calling the api once upon loading.
Is there no easy solution to this? Or an industry way of dealing with this?
here is a minimal working example of what you can do, it's not the best thing in the world, but this is what works for me, let me know if you can make it any better.
The answer to your problem is really simple, BUT, you need to rearrange some stuff first.
A Flutter app can be split into multiple layers which are (just for example) data, state management and UI, in the data layer you will have all methods that communicate with the API, and you call them inside the state management solution (which is provider in your case), the result will be accessible from the provider which will save the data in a variable, then the UI will be able to retrieve these data from the provider, this seems a bit redundant I know, but there is a reason why we do that, if you put the API call inside the provider itself, and there is somewhere else in your app that uses the same endpoint then you will have duplicate code, as for the provider, it's the place where your data is stored in the runtime, these data are what makes the state of your app, finally, the UI can handle displaying data from the provider easily, just make a boolean in the provider that indicates if the API call is executing/loading or not, and inside the consumer in the UI display different widgets based on the boolean.
If we were to visualize the flow of the operation it would be like this:
1- action from the UI that triggers a method from the provider.
2- inside the provider method you will set the boolean indicating that the API call is executing to true and call notifyListeners().
3- call the API request and call .then() on it.
4- inside the .then() set the boolean to false to notify that the call is over and set the received data to a variable inside the provider and call notifyListeners again.
5- in the UI you should have a consumer listening to your provider and handling the boolean, if its true then display a CircularProgressIndicator for example, and if it's false then display your desired widget
Regarding the context in the initState you can fix this problem in 3 ways:
1- using WidgetsBinding.instance
.addPostFrameCallback((_) => yourProviderFunction(context));
2- by registering your provider in a service locator so you don't have to use a context at all. (which is what I used in the example project I posted above)
3- by executing the desired function in the constructor of the provider, so when its initialized the API request will be called
Is this the Academind course?
Also this is the correct way.
For using a Provider you need the context.
EDIT: Added BaselAbuhadrous' comment to the answer.
You need to use didChangeDependencies because the initState actually provides the context, but the screen layout isn't built yet, so you get an error, but if you used WidgetsBindings.instance and call the provider inside of it, then you won't get the error.
//your model , product_model.dart
import 'dart:convert';
List<Product> availableTicketsFromJson(String str) => List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));
class Product {
String title;
String description;
String imageUrl;
double price;
bool isFavorite;
Product(
{this.title,
this.description,
this.imageUrl,
this.price,
this.isFavorite});
factory Product.fromJson(Map<String, dynamic> json) => Product(
title: json['title'] as String,
description: json['description'] as String,
imageUrl: json['imageUrl'] as String,
price: json['price'] as double,
isFavorite: json['isFavorite'] as bool,
);
}
//viewmodel class
final String url = "test.com";
Future<List<Product> fetchProducts() async {
List<Product> products = List<Product>();
try {
final request = await http.get(url);
if(request.statusCode == 200) {
products = productsFromJson(request.body.toString());
notifyListeners();
} else {
print(request.statusCode.toString());
}
} catch(e) {
return List<Product>();
}
return products;
}
//fetch_data.dart
Create your instance of provider in the page that you wanna fetch the data:
Under State<yourWidget>
=> FetchDataViewModel _model;
List<Product> products = [];
under build method
=> _model = Provider.of<FetchDataViewModel>(context,listen: false);
Make a http request with FutureBuilder
FutureBuilder(future:_model.fetchProducts()),
builder: (context,snapshot)){
if(snapshot.connectionState == ConnectionState.done) {
products = snapshot.data;
if(products.length > 0) {
return ListView.builder(
itemCount: products.length,
itemBuilder : (context,index) {
return _items();
}
);
} else {return _noSavedDataWidget();}
}
}
You can test such a code
sometimes
Provider.of<'providerClassName'>(context, listen : false).'providerFunction'
might help.

Apikey authentication webapi core 2.1

I have some service that is going to send some messages to my endpoint. But I need to validate these messages by checking if the http header consist out of a fixed (for a period) api key and id. I can do this by check the header but I don't think this is good practice. Anybody a clue on how to verify that the message send from the service?
I have found something but it is for core2.2 and I need to use 2.1... (https://github.com/mihirdilip/aspnetcore-authentication-apiKey)
Thanks in advance
If you have quite a few endpoints, maybe even multiple controllers i would suggest writing a middleware to handle this.
But if this apikey check is only needed to one endpoint. Since you said "my endpoint".
I would recommend just checking the header value in the controller action/endpoint
Example:
[HttpGet]
public async Task<IActionResult> ExampleEndpoint() {
var headerValue = Request.Headers["Apikey"];
if(headerValue.Any() == false)
return BadRequest(); //401
//your endpoint code
return Ok(); //200
}
You can check the request header in custom middleware as shown here . Or you can use action filter to check the api key , see code sample here .
Like I said I want to do this via the middleware and not in the end of the http pipeline. In the meantime I figured out a solution, it is a simple one but it works.
I created a class called MiddelWareKeyValidation with the following async method:
public async Task Invoke(HttpContext context)
{
if (!context.Request.Headers.Keys.Contains("X-GCS-Signature") || !context.Request.Headers.Keys.Contains("X-GCS-KeyId"))
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("User Key is missing");
return;
}
else
{
var apiKey = new ApiKey { Signature = context.Request.Headers["X-GCS-Signature"], Key = context.Request.Headers["X-GCS-KeyId"] };
if (!ContactsRepo.CheckValidUserKey(apiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Invalid User Key");
return;
}
}
await _next.Invoke(context);
}
Then I go to my Startup.cs in the Configure method where I add a new middleware like so:
app.UseMiddleware<MiddelWareKeyValidation>();
A good resource and credits goes to this article: https://www.mithunvp.com/write-custom-asp-net-core-middleware-web-api/