When using HttpClient.GetFromJsonAsync() or Content.ReadFromJsonAsync<T>() some properties are not parsed correctly.
Chrome debugger parsed the UserSsid property correctly:
But the HttpClient did not:
Blazor-Client code
async Task WaitForExport(string location)
{
while (await periodicTimer.WaitForNextTickAsync())
{
var response = await Http.GetAsync(location, HttpCompletionOption.ResponseContentRead);
if (response != null && response.IsSuccessStatusCode)
{
var exportStatus = await response.Content.ReadFromJsonAsync<ExportStatusModel>();
if (exportStatus != null)
{
return;
}
}
}
}
Controller
[HttpGet("export-status")]
public async Task<ActionResult<ExportStatusModel>> GetExportStatus(Guid guid)
{
var model = await _exportRequestRepository.GetExportStatus(guid);
if (model == null)
return NotFound();
return Ok(model);
}
Model
public class ExportStatusModel
{
public Guid Guid { get; set; }
public int Status { get; set; }
public string UserSsid { get; set; } = string.Empty;
public int DocumentId { get; set; } = -1;
}
I already tried reading the Content as string first to parse it afterwards but this returned a hex string instead of the raw json. Converting this hex string to ascii failed in blazor wasm. In a console application it worked however.
I also noticed that the Content-Length header was missing in the response. Maybe this breaks the json serializer?
The string is in hex:
53 == 'S' and 2D == '-'
You could convert it to a normal string with at least:
string newssid = "";
for (int i = 0; i < ssid.Count(); i+=2)
{
newssid += "" + (char)Convert.ToByte(ssid.Substring(i, 2), 16);
}
I dont know why its in hex though. Maybe you turned on "show as hexadecimal" in Visual studio.
Right click on the Field and check if hexadecimal display is checked!
Related
I started learning C# and I want to update my model using the [HttpPost] annotation. I tried removing the [FromBody]Item itm parameter and the fields on the repository but it's not working either. Below is my code.
Controller:
[HttpPost("{id}")]
public ActionResult<Item> UpdateItem([FromBody]Item itm, int id)
{
var getItem = _repository.GetItemById(id);
if (getItem == null)
{
return NotFound();
}
_repository.UpdateItem(itm);
_repository.SaveChanges();
return Ok(getItem);
}
Repository:
public void UpdateItem(Item itm)
{
if (itm == null)
{
throw new ArgumentNullException(nameof(itm));
}
var itemToUpdate = this.GetItemById(itm.Id);
if (itm.Name != null)
{
itemToUpdate.Name = itm.Name;
}
itemToUpdate.Price = itm.Price;
itemToUpdate.Condition = itm.Condition;
itemToUpdate.Size = itm.Size;
itemToUpdate.DateSold = itm.DateSold;
itemToUpdate.SellMethod = itm.SellMethod;
_context.Items.Update(itemToUpdate);
}
Interface:
void UpdateItem(Item itm);
Model:
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int Price { get; set; }
public string Condition { get; set; }
public string Size { get; set; }
public string DateSold { get; set; }
public string SellMethod { get; set; }
First of all verify that you're sending that item correctly:
Is the form correct and pointing to that method of your controller?
Are you sending that item via the form (have you used the provided methods for this) ?
After that, if you're sending the item in the body of your post request, then verify the item in the method's parameter is available.
EDIT:
Well, as already discussed with Panagiotis you should rather directly use the DbContext itself as it already provides everything you need.
[HttpPost("{id}")]
public ActionResult<Item> UpdateItem(int id, [FromBody]Item itemData)
{
var foundItem = _dbContext.Items.SingleOrDefault(x => x.Id == id);
if (foundItem == null)
{
return NotFound();
}
foundItem.Name = itemData.Name;
foundItem.Size = itemData.Size;
// and so on
_dbContext.SaveChanges();
return Ok(foundItem);
}
Another way to keep your current structure, but it's not recommended, would be the following:
[HttpPost("{id}")]
public ActionResult<Item> UpdateItem(int id, [FromBody]Item itemData)
{
var updatedItem = _repository.UpdateItem(id, itemData);
if (updatedItem == null)
{
return NotFound();
}
return Ok(updatedItem);
}
public void UpdateItem(int id, Item itemData)
{
// you can validate parameters and throw errors if e.g. itemData == null
var originalItem = GetItemById(id); // this should internally get the item e.g. _dbContext.Items.Where(x => x.id == itemData.Id);
if(originalItem == null)
{
return null;
}
originalItem.Name = itemData.Name;
originalItem.Price = itemData.Price;
originalItem.Condition = itemData.Condition;
originalItem.Size = itemData.Size;
originalItem.DateSold = itemData.DateSold;
originalItem.SellMethod = itemData.SellMethod;
SaveChanges(); // guess this will be _dbContext.SaveChanges() instead
return originalItem;
}
Well, you could also change it to first load the item and then pass the originalItem and the itemData into the UpdateItem method inside your repository. But as you see the better way to directly use the DbContext is more clearer and shorter.
I am trying to retrieve an image from a Form, but I always get a null instead.
The data is sent from Form as a parameter to the method AddArticles, that writes to the database after trying to parse the image into a byte array.
[HttpPost]
public async Task<IActionResult> Add([FromForm] ArticleViewModel artice, IFormFile file) {
int result = await AddArticles(artice, file);
if (result == 201 && ModelState.IsValid) {
return RedirectToAction("Index");
} else {
return View(artice);
}
}
public async Task<int> AddArticles(ArticleViewModel articles, IFormFile file {
if (articles != null && articles.Name != null && articles.Article != null && articles.category != null) {
DateTime dateTime = new DateTime();
articles.dateTimeOfCreate = dateTime.Date;
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("Article");
var collection = database.GetCollection<ArticleViewModel>("article");
if (articles.UrlImage != null) {
var ArticleModel = new ArticleModel();
using (var memoryStream = new MemoryStream()) {
file.CopyTo(memoryStream);
articles.UrlImage = memoryStream.ToArray();
//articles.imagecontext = memoryStream.ToArray();
}
await collection.InsertOneAsync(articles);
}
return 201;
} else {
return 400;
}
}
I want to write the image data into the database. After that, I want to convert that byte array nack into an Image and display it.
public class ArticleViewModel {
[BsonId]
public ObjectId _id { get; set; }
[BsonElement("Name")]
public string Name { get; set; }
[BsonElement("Article")]
public string Article { get; set; }
[BsonElement("Image")]
public byte[] UrlImage { get; set; }
[BsonElement("dateTimeOfCreate")]
public DateTime dateTimeOfCreate { get; set; }
[BsonElement("Category")]
public string category { get; set; }
}
I am doing a search in which I am making an API call and get the XML response and SerializeXmlNode and DeserializeObject to my root object. Now the problem is when I tried to loop with foreach.
I get this error below:
foreach statement cannot operate on variables of type (Model.AccountLite) because does not contain public instance definition for 'getenumerator'
I have inspected this data = JsonConvert.DeserializeObject(json); and i can see the data.
I have tried to look at this previously asked question
Search API call
public static List<AccountLite> searchAccounts(string searchString)
{
List<AccountLite> result = new List<AccountLite>();
Root data = new Root();
string[] contains = searchString.Split(' ');
RestClient client = new RestClient(baseUrl);
foreach (string contain in contains)
{
if (contain.Length < 3) continue;
RestRequest request = new RestRequest($"/xx/xx/xx/xxx/xxx/account?xx=Lite&searchString={searchString}");
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
request.AddHeader("Authorization", "Basic " + encoded);
IRestResponse response = client.Execute(request);
string requestResponse = response.Content;
//Converting data from XML into Json and deserializet json object
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(requestResponse);
string json = JsonConvert.SerializeXmlNode(doc);
data = JsonConvert.DeserializeObject<Root>(json);
}
catch (Exception)
{
continue;
}
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
//this line is the one showing error.
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
bool containsBoth = true;
foreach (string contain2 in contains)
{
if (!item.Name.ToLower().Contains(contain2.ToLower()) && !item.Id.ToLower().Contains(contain2.ToLower()))
containsBoth = false;
}
if (containsBoth)
{
if (result.FirstOrDefault(i => i.Id == item.Id) == null)
{
result.Add(item);
}
}
}
}
return result;
}
Model
public class AccountLite
{
public string Id { get; set; }
public string AccountStatus { get; set; }
public string AccountTypeCode { get; set; }
public string Location { get; set; }
public string Name { get; set; }
public string SRIntegrationFlag { get; set; }
}
public class ListOfAccountLite
{
public AccountLite AccountLite { get; set; }
}
public class SiebelMessageEnvelope
{
[JsonProperty("#xmlns")]
public string Xmlns { get; set; }
public ListOfAccountLite ListOfAccountLite { get; set; }
}
public class Root
{
public SiebelMessageEnvelope SiebelMessageEnvelope { get; set; }
}
Json Object
{
"SiebelMessageEnvelope":{
"#xmlns":"",
"ListOfAccountLite":{
"AccountLite":{
"Id":"",
"AccountStatus":"",
"AccountTypeCode":"",
"Location":"",
"Name":"",
"SRIntegrationFlag":""
}
}
}
}
Your ListOfAccountLite just contains a single AccountLite. It doesn't make sense to foreach over a single object, where that object is not enumerable (meaning: implemented IEnumerable[<T>] or contains an explicit GetEnumerator() method).
There's only one object, so... just take it. Instead of
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
// ... do the thing
}
simply
var item = data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite;
if (item is null)
continue;
// ... do the thing
That said: you should probably investigate whether ListOfAccountLite in the JSON etc is meant to be an array rather than a single object.
Response is successful, i can view it in Visual Studio, but when i try to get returned data, its null.
This is API https://yoda-api.appspot.com/api/v1/yodish?text=I%20am%20yoda
And this is my code:
public class YodishModel
{
public string yodish { get; set; }
}
public class YodishResult
{
public YodishModel Result { get; set; }
}
public class YodishService : iService
{
public string GetText(string text)
{
Lazy<RestClient> client = new Lazy<RestClient>(() => new RestClient($"http://yoda-api.appspot.com/api/v1/yodish?text={text}"));
var request = new RestRequest();
var response = client.Value.Execute<YodishResult>(request);
if (response.IsSuccessful)
{
return response.Data.Result.yodish;
}
return null;
}
public string ToUrl(string text)
{
return HttpUtility.UrlEncode(text);
}
}
Response is successful, i can view the result, but Result is null (NullPointerException).
Also, is there a way to use parameters here instead of using string interpolation? 'text' is part of the URL which is officially not a paremeter.
In your case, you were deserializing using a mismatched object. This is what I did to fix it:
public class YodishModel
{
public string yodish { get; set; }
}
public class YodishService
{
public string GetText(string text)
{
Lazy<RestClient> client = new Lazy<RestClient>(() => new RestClient($"https://yoda-api.appspot.com/api/v1/"));
var request = new RestRequest($"yodish").AddQueryParameter("text", Uri.EscapeDataString(text), true);
var response = client.Value.Execute<YodishModel>(request);
if (response.IsSuccessful)
{
return Uri.UnescapeDataString(response.Data.yodish);
}
return null;
}
}
I also added the AddQueryParameter, as you mentioned.
I am new to web applications and to be hones I am trying to update an old web-application, trying to speed it up.
I analyze the application and see what's the reason of the slow in loading pages..
Of course it's the http-requests and I tried to read how can I speed up the requests and I found that I should serialize the data but I don't know how to do that in my application. So I need someone to tell me how to use serialization or how can I speed up the request please:
first it uses angular-js so the request is like that:
viewModelHelper.apiPost('Book/book_getList', null,
function (result) {
$scope.gridOptions.data = result.data;
});
and I use simillar methods to have the data...
The viewModelHelper.apiPost in the app.js:
self.apiPost = function (uri, data, success, failure, always) {
self.modelIsValid = true;
$http.post(MyApp.rootPath + uri, data)
.then(function (result) {
success(result);
if (always != null)
always();
}, function (result) {
if (failure != null) {
failure(result);
}
else {
var errorMessage = result.status + ':' + result.statusText;
if (result.data != null) {
if (result.data.Message != null)
errorMessage += ' - ' + result.data.Message;
if (result.data.ExceptionMessage != null)
errorMessage += ' - ' + result.data.ExceptionMessage;
}
}
if (always != null)
always();
});
}
And then let's say in the bookController there is book_getList method:
public JsonResult book_getList()
{
List<BookModels> List = obj.book_getList();
return Json(List, JsonRequestBehavior.AllowGet);
}
And the bookModels have properties like this:
public Guid guid_book_id { get; set; }
public string code { get; set; }
public string closet { get; set; }
public string shelf { get; set; }
public string title { get; set; }
And the method get_list:
public List<BookModels> book_getList()
{
try
{
OpenCon();
List<BookModels> List = con.Query<BookModels>("book_getList", param: null, commandType: CommandType.StoredProcedure).ToList();
return List.ToList();
}
catch (Exception)
{
throw;
}
finally
{
CloseCon();
}
}
Thanks