Product code causing web mvc api call to fail - c#

I am using mvc5 web API to power a mobile app in xamrian, for the most part, the web API calls work 100% however today I came across a situation that where there was a full stop in the product code caused the call to the web API to fail.
But today I had a product code like this TFNT1116.5 and the API call failed.
public async Task<StockWarehouseInfoBins> GetWarehouseBinsInformaiton(string ItemCode, string WarehouseName)
{
List<StockWarehouseInfoBins> _result = new List<StockWarehouseInfoBins>();
var uri = new Uri(string.Format(BaseUrl + Constants.BinDetails + ItemCode.Trim(), string.Empty));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var byteArray = await response.Content.ReadAsByteArrayAsync();
var content = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
_result = JsonConvert.DeserializeObject<List<StockWarehouseInfoBins>>(content);
}
return _result.FirstOrDefault();
}
My Client is a singleton of an HTTP client class.
HttpClient _client;
I think the problem is to do with the full stop in the product code.
This is the call that it gets to in my MVC controller.
[HttpGet]
// GET: Warhouses/Details/5
public ActionResult BinDetails(string id)
{
List<StockWarehouseInfoBins> result = new List<StockWarehouseInfoBins>();
result = database.GetWarehouseDetails(id);
return Json(result, JsonRequestBehavior.AllowGet);
}
And this is my route information. When I test the call without product codes without full stops in them it works as expected and returns the correct JSON data.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
This is my class here.
public class StockWarehouseInfoBins
{
public StockWarehouseInfoBins();
public long? BinItemID { get; set; }
public string BinName { get; set; }
public long? WarehouseItemID { get; set; }
public long? WarehouseID { get; set; }
public string WarehouseName { get; set; }
public decimal ConfirmedQtyInStock { get; set; }
public decimal QuantityReserved { get; set; }
public decimal QuantityAllocatedBOM { get; set; }
public decimal QuantityAllocatedSOP { get; set; }
public decimal QuantityAllocatedStock { get; set; }
}
I am calling the above in a async method. Which bininfo is getting null when the full stop is encourted.
StockWarehouseInfoBins binInfo = new StockWarehouseInfoBins();
binInfo = await restServices.GetWarehouseBinsInformaiton(lblstockCode.Text, SouceWarehouseName);
I presume I need to URL encode the API calls but is there a way to do this on the HTTP client so I don't need to change every call?

Related

Blazor - Value saved in TimeSpan doesn't go through HttpPost

I am using this class (representing goal) as model:
public class Goal
{
public int Id { get; set; }
public int UserId { get; set; }
public String Name { get; set; }
public List<GoalDay> Days { get; set; } = new List<GoalDay>();
public bool IsAllDayGenerated { get; set; }
public DateTime StartDateGenerated { get; set; }
public TimeSpan LengthTimeGenerated { get; set; }
public int TotalValue { get; set; }
public string Unit { get; set; }
}
Method in service sending data:
public async Task AddGoal(Goal goal)
{
var result = await _http.PostAsJsonAsync<Goal>("api/goal", goal);
if (result.StatusCode != System.Net.HttpStatusCode.OK)
{
_toastService.ShowError(await result.Content.ReadAsStringAsync());
}
else
{
_toastService.ShowSuccess(await result.Content.ReadAsStringAsync());
}
Method in controller receiving data:
[HttpPost]
public async Task<IActionResult> CreateGoal(Goal goal)
{
var user = await _utilityService.GetUser();
goal.UserId = user.Id;
_context.Goals.Add(goal);
await _context.SaveChangesAsync();
return Ok("Goal added days:" + goal.Days.Count);
}
When I send data using HttpPost to server value saved in TimeSpan (in variable LengthTimeGenerated ) is there. In the upper part of picture can be seen printscreen of network traffic.
But afterward in controller data are gone. In the lower part of the picture can be seen printscreen of model zero value of the same variable in controller.
Looks like the classic case of the camel case changes in the System.Text.Json compared to Newtownsoft. (System.Text.Json is case sensitive by default)
Try adding serialization option to use camel case.
private readonly JsonSerializerOptions _jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true };
// usage
public async Task AddGoal(Goal goal)
{
var result = await _http.PostAsJsonAsync<Goal>("api/goal", goal, _jsonSerializerOptions);
//... etc
}
Other example of usage:
var myParsedObject = await JsonSerializer.DeserializeAsync<SomeResponseType>(await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions);

How to redirect to an url which is coming from the database in .NET?

I have a web api built in .NET, within an endpoint i would like to redirect to an url which matches the the code inserted in database. the endpoint takes as entry the code and i am supposed to redirect to the corresponding url. For that i use the Redirect method which actually is not working. i did Console.Write to print if the url is null or empty but it exists. here is the code of my controller :
[HttpGet("{hash}")]
// [ProducesResponseType(302)]
//[ProducesResponseType(404)]
public async Task<IActionResult> redirectUrl(string hash)
{
var t = await new ServiceUrl(_ctx).GetTarget2(hash);
int a = 0;
foreach (Model.Data.DAO.Url i in t)
{
if (i != null)
{
a=a+1;
}
}
if (a==0)
{
return new TimeoutExceptionObjectResult(error: "Not Found",503);
}else
if (DateTime.Compare(DateTime.Now, t.ElementAt(0).ExpireAt) > 0)
{
t.ElementAt(0).state = "expire";
_ctx.Entry(t.ElementAt(0)).State = EntityState.Modified;
await _ctx.SaveChangesAsync();
return new TimeoutExceptionObjectResult(error: "Url expiré",501);
}
string url= t.ElementAt(0).UrlOrigin;
Console.Write(url);
return new Redirect(url);
}
the GetTarget2 method :
public async Task<IEnumerable<Url>> GetTarget2(string hash)
{
var t2 = await _ctx.Url.Where(u => u.UrlShort == hash).ToArrayAsync();
return t2;
}
and the entity :
[Table("Url")]
public class Url
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UrlShort { get; set; }
public string UrlOrigin { get; set; }
public string state { get; set; }
public int Customer_id { get; set; }
public int? targetItemId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime ExpireAt { get; set; }
[JsonIgnore]
public List<Stats> GetStats { get; set; }
public Url()
{
}
public Url(int Id,string UrlShort,string UrlOrigin,string state,int Customer_id,DateTime CreatedAt,DateTime ExpireAt)
{
this.Id = Id;
this.UrlShort = UrlShort;
this.UrlOrigin = UrlOrigin;
this.state = state;
this.Customer_id = Customer_id;
this.CreatedAt = CreatedAt;
this.ExpireAt = ExpireAt;
}
}
when i try to pass a code which is in database i get this : Not found which means it does not find it in database
Update:
the database context declaration :
private readonly DatabaseContext _ctx;
and its definition :
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
}
public DbSet<Url> Url { get; set; }
public DbSet<User> User { get; set; }
public DbSet<Stats> Stats { get; set; }
}
If I understood your question correctly then you are trying to redirect to another action method by reading the URL from the database. If the retured Url belongs to your application and just have the dynamic parameter then you can try RedirectToAction method for the redirect.
Syntax:
return RedirectToAction("ActionName", new { id = 90 });
The will cause a redirect to YourApp/Controller/ActionName/90
All you have to create an action method with argument (if required) and redirect from another action method from where you are getting url.
Alternativly you can also use
return RedirectToAction("Index", new RouteValueDictionary(
new { controller = "NameOfController", action = "Index", id = id } )
);
The problem was the Redirect method which indicates its content was null or empty although i printed the url in terminal successfully. So What i did is adding a prefix (http://) like this Redirect("http://"+url), url is the url coming from the database. if the url already contains the prefix http or https i remove it before passing it as parameter inside Redirect method. Doing this solved my problem.

ASP.NET - API Url to List of Model Objects

Currently I am doing an API call via jQuery, my question is, is there away to do this call in C# or away to convert the results of an API call to an ASP.NET List of Model Objects?
Here is my Model
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public string league { get; set; }
public string division { get; set; }
}
And here is my current ajax call
$.ajax({
url: "https://statsapi.web.nhl.com/api/v1/teams?sportId=1",
success: function (data) {
for (var team of data.teams) {
console.log(team.name);
}
}
});
UPDATE
I changed my classes to look like so:
public class StatsTeamsClass
{
public IEnumerable<Teams> teams { get; set; }
public string copyright { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Teams
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
public League league { get; set; }
public Division division { get; set; }
}
and created this method which indeeds puts the results in model object:
public async System.Threading.Tasks.Task<StatsTeamsClass> GetTeams()
{
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
But when I try to call this method in another controller, it just hangs there, no error, no nothing, I am assuming it will just time out after a while
public class HomeController : Controller
{
APIController webService = new APIController();
public ActionResult Index()
{
var item = webService.GetTeams().Result.teams;
return View();
}
}
(GetTeams() is inside the controller APIController)
So what would be the proper way to A. get the results of an API in object model and then call those results?
The controller action needs to be made async as well to avoid mixing async-await and blocking calls like .Result or .Wait() that could potentially cause deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
public class HomeController : Controller {
APIController webService = new APIController();
public async Task<ActionResult> Index() {
var model = await webService.GetTeams();
var teams = model.teams;
return View();
}
}
Assuming APIController is an actual ApiContoller
public class APIController : ApiController {
//Your original code
public async Task<StatsTeamsClass> GetTeams() {
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
//...
}
I would suggest not calling APIController directly like that from the HomeController and instead extract the GetTeams() method out into a reusable service
public class WebService {
static Lazy<HttpClient> http = new Lazy<HttpClient>();
public async Task<T> GetAsync<T>(string url) {
var json = await http.Value.GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(json);
}
public Task<StatsTeamsClass> GetTeamsAsync() {
var url = "https://statsapi.web.nhl.com/api/v1/teams?sportId=1";
return GetAsync<StatsTeamsClass>(url);
}
}
Reference You're using HttpClient wrong
that can be properly used in HomeController
public class HomeController : Controller {
public async Task<ActionResult> Index() {
// Ideally web service should be injected but that topic
// is outside of the scope of the question at the moment.
var webService = new WebService();
var model = await webService.GetTeamsAsync();
var teams = model.teams;
//...
return View(teams);
}
}
The assumption here is that the project is a mixed Asp.Net MVC and Web Api 2+
Index.cshtml
#model IEnumerable<Teams>
#{
ViewBag.Title = "Teams";
}
#if(Model != null && Model.Count() > 0) {
#foreach (var #team in Model) {
<p>#team.name</p>
}
}
Yes, the equivalent in C# would be to use HttpClient. You're best off creating a static instance of the class that you reuse for a particular kind of repeated call:
private static readonly HttpClient Http = new HttpClient();
and then used it from an async method using Newtonsoft.Json like this:
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
You can then parse this string of JSON into a model class like this:
var model = JsonConvert.DeserializeObject<TeamStatsClass>(json);
As the question is answered by #Daniel above just want to add couple of more points here The json you are getting cannot be directly casted to TeamStatsClass you might have to introduce another base class as teams is the collection in the json you are getting.
Im posting it here to get a clearer view
public class ResponseBaseClass
{
public IEnumerable<TeamStatsClass> teams { get; set; }
public string copyright { get; set; }
}
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public Division division { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string nameShort { get; set; }
public string link { get; set; }
}
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
var model = JsonConvert.DeserializeObject<ResponseBaseClass>(json);
var yourTeamModelObj = model.teams;

MVC Web API Post method with custom action name

And I wrote custom action names for ever process.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In webapi config I wrote like this and in controller, I started like below
[System.Web.Http.HttpPost]
public HttpResponseMessage Control(List<string>codes,string updateperson,string refPeriod, int actualyear, int actualmonth)
{
For get methods, everything works well but for post method it doesn't work and gives error like below.
In body in post I send
{codes: ["CPM2018-004"], updateperson: "E3852", refPeriod: "SFC18", actualyear: 2018, actualmonth: 12}
Request URL:http://localhost:50941/Api/Investment/Control Request
Method:POST Status Code:404 Not Found Remote Address:[::1]:50941
Referrer Policy:no-referrer-when-downgrade
How can I sreceive post requests to web API with custom action name?
Create model to hold value being posted.
public class ControlViewModel {
public List<string> codes { get; set; }
public string updateperson { get; set; }
public string refPeriod { get; set; }
public int actualyear { get; set; }
public int actualmonth { get; set; }
}
And then update the action to expect the data in the BODY of the request
public class InvestmentController : ApiController {
[HttpPost]
public HttpResponseMessage Control([FromBody]ControlViewModel data) {
//...
}
//...
}
Reference Parameter Binding in ASP.NET Web API

Post WebApi Method With Parameter C#

I would like to just post Webapi method in asp.net mvc the post action method looks like
[HttpPost]
[Route("api/agency/Dashboard")]
public HttpResponseMessage Index(getCookiesModel cookies)
{
//code here
}
and I am sending post request like this
string result = webClient.DownloadString("http://localhost:11668/api/agency/dashboard?cookies=" + cookies);
and the getCookiesModel
public class getCookiesModel
{
public string userToken { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public long userId { get; set; }
public string username { get; set; }
public string country { get; set; }
public string usercode { get; set; }
}
But this return 404 page not found.
Please help me how to solve this.
DownloadString is a GET request and since the action is expecting a POST, you can see where that may be a problem.
Consider using HttpClient to post the request. If sending the payload in the body then there is no need for the query string, so you also need to update the client calling URL.
var client = new HttpCient {
BaseUri = new Uri("http://localhost:11668/")
};
var model = new getCookiesModel() {
//...populate properties.
};
var url = "api/agency/dashboard";
//send POST request
var response = await client.PostAsJsonAsync(url, model);
//read the content of the response as a string
var responseString = await response.Content.ReadAsStringAsync();
The web API should follow the following syntax
[HttpPost]
[Route("api/agency/Dashboard")]
public IHttpActionResult Index([FromBody]getCookiesModel cookies) {
//code here...
return Ok();
}

Categories

Resources