C#, NetCore 3.0 - Method : [HttpGet] - c#

I have a problem with retrieving data from an array for a specific user - there are many users in the array, I want to retrieve data for one specified in label1.Text
So - the first:
public async Task load_data()
{
var doing = await client.GetAllAsync(label1.Text);
foreach (var dane in doing )
{
lbItems_1.Items.Add(dane.Name);
lbItems_2.Items.Add(dane.Age);
lbItems_3.Items.Add(dane.Data);
}
}
Next : GetAllAsync()
public async Task<IEnumerable<Table_test>> GetAllAsync(string user_name)
{
var response = await client.GetAsync(_baseUrl + "/api/user/" + user_name);
}
And the last class:
[HttpGet]
public async Task<IActionResult> Get(string user_name)
{
using (var c = new MySqlConnection(con_sql.MySQL))
{
var sql = #"SELECT * FROM table_test WHERE username = #username";
var query = c.Query<Models.Table_test >(sql, new { username = user_name}, commandTimeout: 30);
return Ok(query);
}
}
I think it passes the parameter well - however when I try to download data for the appropriate user I have: NotFound code
Why? Ideas?
Thank you.

You should be able to specify the route parameter on the endpoint in the HttpGet attribute:
[HttpGet("{user_name}")]
public async Task<IActionResult> Get(string user_name)
{
using (var c = new MySqlConnection(con_sql.MySQL))
{
var sql = #"SELECT * FROM table_test WHERE username = #username";
var query = c.Query<Models.Table_test >(sql, new { username = user_name}, commandTimeout: 30);
return Ok(query);
}
}
Another option would be to call the endpoint using a query parameter:
public async Task<IEnumerable<Table_test>> GetAllAsync(string user_name)
{
var response = await client.GetAsync(_baseUrl + "/api/user?user_name=" + user_name);
}

Related

.NET Core API - Return Image URL from Server and not from local storage

In Chrome i get following error:
Not allowed to load local resource: C://...
Now i want to change the return from the Image in like 'localhost:44346/wwwroot/images/profileimages/img.jpg'
Can you tell my how i can do this?
This is my Controller for the Fileupload:
[HttpPut]
[Authorize]
[RequestSizeLimit(1_000_000)]
[Route("UpdateImage")]
public async Task<IActionResult> UpdateImage([FromForm]ApplicationUserModel model)
{
try
{
var user = await _userManager.FindByIdAsync(model.id);
if (user.ProfileImagePath != null)
{
var file = new FileInfo(user.ProfileImagePath);
file.Delete();
}
var uniqueFileName = GetUniqueFileName(model.ProfileImage.FileName);
var uploads = Path.Combine(hostingEnvironment.WebRootPath, "Images\\ProfileImages");
var filePath = Path.Combine(uploads, uniqueFileName);
await model.ProfileImage.CopyToAsync(new FileStream(filePath, FileMode.Create));
user.ProfileImagePath = filePath;
var result = await _userManager.UpdateAsync(user);
return Ok(result);
}
catch (Exception ex)
{
throw ex;
}
}
This is the Controller for getting the User Informations:
[HttpGet]
[Authorize]
[Route("GetCurrentUser")]
public async Task<IActionResult> GetCurrentUser()
{
try
{
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
return Ok(user);
}
catch (Exception ex)
{
throw ex;
}
}
So i have found a solution for my problem.
When i trigger my GetCurrentUser function than i will check i the FilePath starts with 'C'.
When this is true i will format the filepath with my localhost address. Like Bruno suggested.
But this works only if i have the UseStaticFiles() in my Startup.cs
app.UseStaticFiles();
This is not beautiful but it does the work and its only for testing.
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
if (user.ProfileImagePath.StartsWith('C'))
{
var url = "https://localhost:44356/";
user.ProfileImagePath = user.ProfileImagePath.Remove(0,58);
user.ProfileImagePath = url + user.ProfileImagePath;
}
return Ok(user);

HttpClient GET method with string parameter

I'm making an app that'll host database and communicate with web site and wpf app via Web API. So, the problem is this: how can I make GET request with string parameter? I already did similar thing, but with integer parameter. Almost the same code does not work for string parameter. Why?
Code that works with int param:
Controller method:
[HttpGet]
[ActionName("GetResItems")]
public IHttpActionResult GetResItems(int id)
{
using (var db = new FoodOrderingContext())
{
List<ItemsInRestaurant> list = db.ItemsInRestaurants.Where(x => x.ItemId == id).ToList();
List<ItemsInRestaurantVM> toSend = new List<ItemsInRestaurantVM>();
foreach (var item in list)
{
toSend.Add(new ItemsInRestaurantVM(item));
}
if (toSend.Count == 0)
{
return NotFound();
}
return Ok(toSend);
}
}
Code on the client WPF side:
public List<ItemsInRestaurantVM> GetResItems(int id)
{
List<ItemsInRestaurantVM> list = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:52407/");
var responseTask = client.GetAsync("api/item/getresitems/" + id);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readingTask = result.Content.ReadAsAsync<List<ItemsInRestaurantVM>>();
readingTask.Wait();
list = readingTask.Result;
}
}
return list;
}
Now, the code that does NOT work:
Server side:
[HttpGet]
[ActionName("GetTop10Items")]
public IHttpActionResult GetTop10Items(string type)
{
using (var db = new FoodOrderingContext())
{
List<Top10ItemsVM> list = new List<Top10ItemsVM>();
foreach (var item in db.Items.ToList())
{
if (item.Type.Equals(type))
{
Top10ItemsVM toAdd = new Top10ItemsVM();
toAdd.Name = item.Name;
toAdd.Price = item.Price.ToString() + " $";
toAdd.Ammount = db.OrderItems.Where(x => x.ItemId == item.ItemId).Select(x => x.Ammount).Sum().ToString();
toAdd.Total = (db.OrderItems.Where(x => x.ItemId == item.ItemId).Select(x => x.Ammount).Sum() * item.Price).ToString() + " $";
list.Add(toAdd);
}
}
if (list.Count > 0)
{
return Ok(list);
}
else
{
return NotFound();
}
}
Client side:
public List<Top10ItemsVM> GetTop10Items(string type)
{
List<Top10ItemsVM> list = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:52407/");
var responseTask = client.GetAsync("api/item/GetTop10Items/" + type);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<List<Top10ItemsVM>>();
readTask.Wait();
list = readTask.Result.OrderByDescending(x => x.Ammount).Take(10).ToList();
}
return list;
}
}
First part works like it should be. The second one does not even call the server method. You can clearly see it's almost the same code. What am I missing here?
Route parameter are transmitted via ?param=value. Your first case only works because the asp.net template adds a default route map which looks something like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Which defines that every Action method which has a parameter id defined, maps automatically to: myurl/action/<id>.
To make your second case work you have to call the action like this:
api/item/GetTop10Items?type=something
where you explicitly define the route parameter type and its value.

How to consume DELETE request from windows client application

In my web API delete request, which containing multiple parameters.I need to consume this DELETE request using C# windows form application and my code as bellow.
private void btnDelete_Click(object sender, EventArgs e)
{
using (var client = new HttpClient())
{
person p = new person { ID = 1, SID = 5, Name = "paul"};
client.BaseAddress = new Uri("http://localhost:2733/");
var response = client.DeleteAsync("api/person/").Result;
if (response.IsSuccessStatusCode)
{
Console.Write("Success");
}
else
Console.Write("Error");
}
}
This is how I consume this using Postman, and its works finehttp://localhost:2733/api/person/1/5/"paul" How to consume this using my windows client. I try these two way,
var response = client.DeleteAsync("api/person/",p).Result;
and
var response = client.DeleteAsync("api/person/"+1+5+"paul").Result;
But those are not working. How can I pass parameters to DELETE request.
Updated:
This is my controller class,
[Route("api/person/{id:int}/{pid:int}/{pname}")]
[HttpDelete]
public void Delete(int id, int pid, string pname)
{
var pModel = new PModel
{
ID = id,
SID = pid,
Name= pname
};
Person p = new Person();
p.deletePerson(pModel);
}
This is Person class
public void deletePerson(PModel p)
{
try
{
string sql = $"DELETE from person WHERE ID = {p.ID} AND SID = {p.SID} AND Name= {p.Name}"; ;
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(sqlString, conn);
cmd.ExecuteNonQuery();
long x = cmd.LastInsertedId;
}
catch (MySqlException x)
{
int errr = x.Number;
Console.WriteLine(errr);
}
}
Try the below. It should replicate the way you have tried consuming your API through PostMan.
var response = client.DeleteAsync($"api/person/{p.ID}/{p.SID}/\"{p.Name}\"").Result;

MVC 6 asynchronous action

So I've started my proof-of-concept for converting my nHibernate website to using Dapper.
My action method that I'm working appears to be working right now:
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = invoice.Result,
AllInvoices = allInvoices.Result
};
return View(model);
}
But then I realized/remembered, that in order for it to be asynchronous, I need to have Task on the action, like this:
public Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = invoice.Result,
AllInvoices = allInvoices.Result
};
return View(model);
}
But as soon as I do that, it tells me that I need to await something. In all of the examples I've seen, it just shows something like this:
var result = await repo.Get(param);
But I'm already doing the "awaiting" in my repos.
public async Task<Invoice> GetInvoiceAsync(int invoiceId)
{
const string query = "select InvoiceId, Name [InvoiceName] from dbo.Invoice where InvoiceId = #invoiceId";
using (var conn = GetConnection())
{
var dp = new DynamicParameters();
dp.Add("#invoiceId", invoiceId);
await conn.OpenAsync();
var invoiceResult = await conn.QueryAsync<Invoice>(query, dp, null, 30, CommandType.Text);
var invoice = invoiceResult.SingleOrDefault();
return invoice;
}
}
public async Task<List<Invoice>> GetAllInvoicesAsync()
{
const string query = "select InvoiceId, Name [InvoiceName] from dbo.Invoice where SalesPeriodId >= 17";
using (var conn = GetConnection())
{
await conn.OpenAsync();
var invoiceResult = await conn.QueryAsync<Invoice>(query, null, null, 30, CommandType.Text);
var invoices = invoiceResult.Take(1000).ToList();
return invoices;
}
}
So the whole point in me switching to asynchronous is to be able to do both of my calls asynchronously, then merge the results together when returning.
How do I change my controller action to do this asynchronously, while having the Task<IActionResult>? Like this:
public Task<IActionResult>About() {}
Update: is this correct then?
public async Task<IActionResult> About()
{
ViewData["Message"] = "Your application description page.";
var invoice = invoiceRepo.GetInvoiceAsync(19992031);
var allInvoices = invoiceRepo.GetAllInvoicesAsync();
var model = new Models.AboutModel
{
Invoice = await invoice,
AllInvoices = await allInvoices
};
return View(model);
}
Will this do both repo calls asynchronously (in parallel)?
You need to await in your controller too. Rule of thumb: Never say .Result, instead say await.
You should also declare your action method as public async as well.
Update: That would be the correct way to asynchronously call your repositories. The database calls should happen in parallel because both tasks are started before anything is awaited. You can always see this yourself by putting debug logging at the start and end of your DB methods and seeing that you get "start 1 start 2 end 1 end 2" or something similar instead of "start 1 end 1 start 2 end 2" if your queries are reasonably slow.

Load text from web during xml parsing in Windows 8 Async

I have a unique issue, I want to get the name of an application from it's AppID while I convert an XML file into objects. This is the code I'm using at present:
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = GetName(query.Value).ToString(),
};
itemGridView.DataContext = data;
}
This is the code I'm using to convert the GUID into an app name using Microsoft's Store API. I can confirm that it does return the app name. I'm just unsure how I can get this to display.
private async Task<string> GetName(string guid)
{
try
{
string link = "https://services.apps.microsoft.com/browse/6.2.9200-1/615/en-NZ_en-NZ/c/NZ/cp/10005001/Apps/{0}";
string url = string.Format(link, guid);
var httpClient = new HttpClient();
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(httpRequestMessage);
var xmlString = await response.Content.ReadAsStringAsync();
XmlDocument NameXML = new XmlDocument();
NameXML = await XmlDocument.LoadFromUriAsync(new Uri(url));
string sAppName = NameXML.GetElementsByTagName("T")[0].ChildNodes[0].NodeValue.ToString();
return sAppName;
}
catch(Exception)
{
return guid;
}
}
I think my problem is with the async / await tasks. I've just been exposed to it now... how would I load up the App Name alongside the AppID when I parse the xml file?
The output that's being displayed when I run the app is "System.Threading.Tasks.Task[System.String]" (The objects load and the links and everything works fine, its just that the above is displayed instead of the app name).
I've been debugging using breakpoints, it appears that the GetName method only seems to be triggered later on, I'm not sure however.
Try to change this line :
AppName = GetName(query.Value).ToString(),
To this :
AppName = await GetName(query.Value),
GetName will return Task<string> instead of string when not awaited. And the method where above code resides required to be async because of using await inside that method :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = await GetName(query.Value),
};
itemGridView.DataContext = data;
}
....
}
UPDATE :
As you already noticed, LINQ has very limited support for async/await currently. So to workaround this limitation, we can use normal for loop to avoid calling async function inside LINQ :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var query = from query in xdoc.Descendants("AppID")
select query.Value;
var data = new List<App>();
foreach (var q in query)
{
data.Add(new App{ AppId = q, AppName = await GetName(q) });
}
itemGridView.DataContext = data;
}
....
}

Categories

Resources