I am new to Json and have to deserialize some Json data received from a url.
I am using an API provided for me to log a user into an application.
The API:
Logging in
To login, make the following call
<site>/Users/login/username:<email>/password:<password>.json
• Success Response
{
“response”:{
“sessionId” : “<sessionId>”,
“businesses” : [{
“<bizId-1>” : “<bizName-1>”,
“<bizId-2>” : “<bizName-2>” ,
“<bizId-n>” : “<bizName-n>”
}]
},
“messages” :{"msgs":"","errs":""}
}
An example of an actual response is as below.
{
"response":{
"sessionId":"50b702d8-78dc-4d65-9de8-2510c327a7be",
"businesses":[
{"50c657af0ad8-4ce7-bb08-1d60c327a7be":"All of The Lights"},
{"50cf705a-ded4-4c7d-95df-51c8c327a7be":"Tomatoes Inc"},
{"50d2cf88-e664-4103-99f9-1aa0c327a7be":"Joe's Jalepinos"},
{"50d2d9b9-c358-4129-b9ec-1aa0c327a7be":"Pizza Place"},
{"50eed93e-f49c-4dff-8c7a-33f0c327a7be":"Samsung"},
{"51036f10-e9c0-47ecb73d-0f50c327a7be":"Peppers"},
{"51036fcd-a6b8-4103-8e160f82c327a7be":"Puppy"}
]
},
"messages":{"msgs":"","errs":""}
}
I have the following code:
try
{
serverUrl = "https://eko-app.com/Users/login/username:" + usernameEntered + "/password:" + passwordEntered + ".json";
var w = new WebClient();
var jsonData = string.Empty;
// make the login api call
jsonData = w.DownloadString(serverUrl);
if (!string.IsNullOrEmpty(jsonData))
{
var dataResult = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonData);
// need help here
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I first want to retrieve the session Id from the response. If it exists, then a user is allowed to login, if the session id is null then login is rejected. How do I do this?
Also, once a user is logged in, I would like to create a list showing the number of businesses per user and their respective business Ids.
Any help appreciated.
To do it in a strongly typed way create a class that mirrors the Json you want to deserialise (only the properties you are interested in are necessary).
class LoginResponseDetails
{
public string SessionId {get;set;} // this might be better as a GUID
public Hashtable Businesses {get;set;} // this could be Dictionary<string, string> or Dictionary<Guid, string>
// other properties here...
}
class LoginResponse
{
LoginResponseDetails Response {get;set;}
// other properties here...
}
Then deserialise to this class:
var response = Newtonsoft.Json.JsonConvert.DeserializeObject<LoginResponse>(jsonData);
var sessionId = response.Response.SessionId;
etc...
You are almost there, simply need to look at the properties of the anonymous type.
Try something like
if (!string.IsNullOrEmpty(jsonData))
{
var dataResult = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonData);
if (dataResult != null && dataResult.response.sessionId != null)
{
// logged in
// iterate and show bussiness list
} else {
Console.WriteLine("Login failed");
}
}
See the JSON.NET docs - http://james.newtonking.com/json/help/html/DeserializeAnonymousType.htm
Related
I am a total noob in C# and tried to add an automated mail services to my backend API for an Angular FrontEnd.
It works properly as intended for one time, but cannot be used a second time. I guess I am violating some object rules, maybe someone is able to point out my mistake.
This is an excerpt of my file UserAuthController.cs which includes the register function. When registration on my website is successful it shall also call the API Service from my automated mail system.
I didn't know how to include the function properly so I've added it with a new namespace.
namespace Gogo_Api.Controllers
{
[RoutePrefix("UserAuth")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class UserAuthController : ApiController
{
private readonly IUserAuthManager _userAuthManager;
public UserAuthController()
{
}
public UserAuthController(IUserAuthManager userAuthManager)
{
this._userAuthManager = userAuthManager;
}
[HttpPost]
[Route("Register")]
public HttpResponseMessage Register(UserDetails user)
{
var response = new RegisterResponse();
response.code = _userAuthManager.Register(user);
if (response.code == 1)
{
response.message = "Registration successful ";
//Including API Service here...
Sendinblue.Program RegisterMail = new Sendinblue.Program();
RegisterMail.Main(user.email, user.displayName, user.country);
RegisterMail = null;
}
else if (response.code == 2)
{
response.message = "User already registered ";
}
else if (response.code == 0)
{
response.message = "Error occured";
}
return Request.CreateResponse(HttpStatusCode.OK, response);
}
}
}
namespace Sendinblue
{
class Program
{
public void Main(string userMail, string userName, string userCountry)
{
Configuration.Default.ApiKey.Add("api-key", "MYKEY");
var apiInstance = new ContactsApi();
string email = userMail;
JObject attributes = new JObject();
attributes.Add("USERNAME", userName);
attributes.Add("COUNTRY", userCountry);
List<long?> listIds = new List<long?>();
listIds.Add(5);
try
{
var createContact = new CreateContact(email, attributes, emailBlacklisted, smsBlacklisted, listIds, updateEnabled, smtpBlacklistSender);
CreateUpdateContactModel result = apiInstance.CreateContact(createContact);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Console.WriteLine(e.Message);
}
}
}
}
I have added 'RegisterMail = null;' because I thought I need to delete my object first before using it again, but still it works only for the first time.
How would I be able to call my function multiple times?
Thanks
Thanks #Richard Barker, your comment helped me fix it.
I have moved everything out of the Controller and had to move
Configuration.Default.ApiKey.Add...
apiInstance = new ContactsApi();
to a one-time call in the Initializer, so the API Call and ContactsApi is only created once, instead of everytime.
I have a Web API (POST) which accepts the input JSON and does operation over it. Due to model state binding, the request by default getting bound to the request model.
We are facing a scenario where in the received JSON is not as per the expected format. Like we are having additional key-value pairs which we want to identify and notify about it. Due to model state binding I'm not able to find the additional parameters.
I have been trying the below code but I do not get the actual request. Is there a way to get the actual request rather than the overridden request.
public override void OnActionExecuting(HttpActionContext actionContext)
{
string uri = actionContext.Request.RequestUri.ToString();
uri = uri.Substring(uri.LastIndexOf('/') + 1).ToLower();
if(uri.Contains("xxx"))
{
PartnerLoginSchema reqSchema = new PartnerLoginSchema();
JsonSchema schema = JsonSchema.Parse(reqSchema.schemaJson);
var requestInput = actionContext.ActionArguments["requestx"];// receiving overriden request
string valid = JsonConvert.SerializeObject(requestInput);
JObject jsonObj= JObject.Parse(valid);
bool testcheck = person.IsValid(schema);
}
}
Eg: Expected JSON
{
req1: "asd",
req2: "wer"
}
Input JSON Received:
{
req1:"asdf",
req2:"werr",
req3:"unwanted" // this attribute is not required and has to be identified
}
I would want to find the req3 present in the JSON by some means.
Is there a way to achieve it in ASP.NET C#?
I'm able to achieve it by reading the input JSON from HttpContext.Current.Request.InputStream
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
string req_txt;
using (StreamReader reader = new StreamReader(HttpContext.Current.Request.InputStream))
{
req_txt = reader.ReadToEnd();
}
try
{
ExpectedJsonFormat s =
JsonConvert.DeserializeObject<ExpectedJsonFormat>(req_txt,
settings); // throws expection when over-posting occurs
}
catch (Exception ex)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, BadAndUnAuthorisedRequest("extra column"));
}
I have a POST method which will return the list of items from the user and since I am very new to c# web api, I am having a hardtime putting the right condition and response if the Id is null, empty or invalid. I've tried similar response and it doesn't work mainly because those examples are using iHttpActionResult instead of List<>
here is the code in my controller which I am not sure what to place on the comments I provided:
[HttpPost]
public List<ValueStory> UserValueStories ([FromBody] ValueStory valuestory)
//public void UserValueStories([FromBody] ValueStory Id)
{
if (valuestory.Id == "" || valuestory.Id == null)
{
//what code to add to change status code to 400 and to display error message?
}
//what code to put if the id is not valid, what status code and what message?
var valueStoryName = (from vs in db.ValueStories
where vs.Id == valuestory.Id
select vs).ToList();
List<ValueStory> vs1 = new List<ValueStory>();
foreach (var v in valueStoryName)
{
vs1.Add(new ValueStory()
{
Id = v.Id,
ValueStoryName = v.ValueStoryName,
Organization = v.Organization,
Industry = v.Industry,
Location = v.Location,
AnnualRevenue = v.AnnualRevenue,
CreatedDate = v.CreatedDate,
ModifiedDate = v.ModifiedDate,
MutualActionPlan = v.MutualActionPlan,
Currency = v.Currency,
VSId = v.VSId
});
}
return vs1.ToList();
}
Appreciate some help and some directions on how to do this correctly.
Change your return type to IHttpActionResult.
To return a 400 BAD REQUEST, return BadRequest().
To return a 404 NOT FOUND, return NotFound().
To return your list data, return Ok(vs1).
See the documentation for more information.
Optional: If you are using a documentation tool like Swagger or the Web Api Help Pages, also add the [ResponseType(typeof(List<ValueStory>))] attribute to your action method.
Method would need to be updated to allow for that level of flexibility
[HttpPost]
[ResponseType(typeof(List<ValueStory>))]
public IHttpActionResult UserValueStories ([FromBody] ValueStory valuestory) {
if (valuestory.Id == "" || valuestory.Id == null) {
//what code to add to change status code to 400 and to display error message?
return BadRequest("error message");
}
var valueStoryName = (from vs in db.ValueStories
where vs.Id == valuestory.Id
select vs).ToList();
var vs1 = new List<ValueStory>();
foreach (var v in valueStoryName) {
vs1.Add(new ValueStory() {
Id = v.Id,
ValueStoryName = v.ValueStoryName,
Organization = v.Organization,
Industry = v.Industry,
Location = v.Location,
AnnualRevenue = v.AnnualRevenue,
CreatedDate = v.CreatedDate,
ModifiedDate = v.ModifiedDate,
MutualActionPlan = v.MutualActionPlan,
Currency = v.Currency,
VSId = v.VSId
});
}
return Ok(vs1);
}
If you really want to keep your return data type (which I think you shouldn't, do as stated in the other answer), then you can throw exceptions as described in Exception Handling in ASP.NET Web API:
To throw a simple exception with specific HTTP code use:
throw new HttpResponseException(HttpStatusCode.NotFound);
To specify message you can do as follows:
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No product with ID = {0}", id)), ReasonPhrase = "Product ID Not Found"
}
throw new HttpResponseException(resp);
As per the knowledge I have on Web API, in the POST method you have to return the result of the post call instead(or along with) of List.
Its better to create a new model which will store the data (List) and result of the POST call (error message and status code).
Based on the Id, you can add respective error message and code.
In case of invalid data, you can make data as null.
The model may look like this.
class Model{
string errorMsg,
string statusCode,
List<ValueStory> data
}
I've read many stackoverflow posts with the similar problems as well as several blogs but I am still uncertain as how to solve my problem :(
I have angularJS directive that allows to upload files to the server. The code is like this:
[HttpPost]
[Route("UploadFile")]
public async Task<HttpResponseMessage> UploadFile()
{
// Check if the request contains multipart/form-data.
if (Request.Content.IsMimeMultipartContent("form-data"))
{
try
{
var resultOut = new List<FileUploadResult>();
var streamProvider = new MultipartMemoryStreamProvider();
streamProvider = await Request.Content.ReadAsMultipartAsync(streamProvider);
foreach (
var item in
streamProvider.Contents.Where(c => !string.IsNullOrEmpty(c.Headers.ContentDisposition.FileName))
)
{
FileUploadResult file = new FileUploadResult()
{
FileName = item.Headers.ContentDisposition.FileName,
// Content = fileBytes, // No need to pass the info back as we're not going to read it save it yet
Key = Guid.NewGuid().ToString(),
Type = item.Headers.ContentDisposition.DispositionType
};
resultOut.Add(file);
//using (Stream stFileSource = new MemoryStream(await item.ReadAsByteArrayAsync())) {
// byte[] fileBytes;
// fileBytes = new Byte[stFileSource.Length];
// stFileSource.Read(fileBytes, 0, Convert.ToInt32(stFileSource.Length));
// FileUploadResult file = new FileUploadResult()
// {
// FileName = item.Headers.ContentDisposition.FileName,
// // Content = fileBytes, // No need to pass the info back as we're not going to read it save it yet
// Key = Guid.NewGuid().ToString(),
// Type = item.Headers.ContentDisposition.DispositionType
// };
// resultOut.Add(file);
//}
}
return Request.CreateResponse(HttpStatusCode.OK, resultOut.ToArray());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Also directive saves the Files array into a property. My user form allows to remove some files / add more files and then I want to save the information from the form (somewhat complex view model) along with the files. I was unable to figure that problem so far. One possibility I see here is to save the files in the UploadFile method using Repository into a database. However, I would prefer to save that into some temporary table instead (e.g. #FileInfo table) and not the actual table. Or perhaps there is a way to save files (with its binary content) into some memory object so I will be able to get that content back when I am ready to save my model's data? Can you either show implementation of the temporary repository storage or give some other ideas for my dilemma?
Firstly, Your directive need to create a post request with 'multipart/form-data'.
Check this link for reference.
However, we use angular file upload to do this.
angular
.module('app', ['angularFileUpload'])
.controller('AppController', function($scope, FileUploader) {
$scope.uploader = new FileUploader(
{
url: 'Your/upload/url',
headers: {
'autorization': 'Bearer token if you need it'
},
onProgressItem: function () {
...
},
onSuccessItem: function (opt, data) {
...
},
onErrorItem: function (opt) {
...
}
});
//you may want to wrap the following in an event
var uploadItem = $scope.uploader.queue[uploader.queue.length - 1];
uploadItem.formData.push({
someData: "someData",
moreData: "moreData"
});
uploadItem.upload();
uploadItem.formData = [];
});
Then in your controller, you can do the following to retrieve what you need:
//your request
var request = HttpContext.Current.Request;
//your fields
var someData = request.Form["someData"];
var moreData = request.Form["moreData"];
//your file
var file = request.Files["file"];
Looks like a job for TempData:
TempData in ASP.NET MVC is basically a dictionary object derived from
TempDataDictionary. TempData stays for a subsequent HTTP Request as
opposed to other options (ViewBag and ViewData) those stay only for
current request. So, TempdData can be used to maintain data between
controller actions as well as redirects.
example:
//Controller Action 1 (TemporaryEmployee)
public ActionResult TemporaryEmployee()
{
Employee employee = new Employee
{
EmpID = "121",
EmpFirstName = "Imran",
EmpLastName = "Ghani"
};
TempData["Employee"] = employee;
return RedirectToAction("PermanentEmployee");
}
//Controller Action 2(PermanentEmployee)
public ActionResult PermanentEmployee()
{
Employee employee = TempData["Employee"] as Employee;
return View(employee);
}
I have been stuck on this for a couple hours now and not even google can help anymore. I am trying to send a file from the client to the backend using xmlhttprequest. I cannot get the filename, type, or content on the C# side. I would appreciate help on doing this. A lot of code I came across had methods that I can only guess are not supported in ASP.Net 5 and MVC 6 (such as HttpContext.Current and HttpPostedFile)
Here is my client side JavaScript request. This sends the query strings which bind to my model no problem so that is easily accessible, but getting the file is what I am having trouble with.
var form = new FormData();
form.append("file", file);
var queryParams = "id=" + (id == null ? -1 : id);
queryParams += "&name=" + name;
queryParams += "&value=" + val;
xhrAttach(REST_DATA + "/attach?" + queryParams, form, function (item) {
console.log('attached: ', item);
alert(item.responseText);
row.setAttribute('data-id', item.id);
removeProgressIndicator(row);
setRowContent(item, row);
}, function (err) {
console.log(err);
//stop showing loading message
stopLoadingMessage();
document.getElementById('errorDiv').innerHTML = err;
});
function xhrAttach(url, data, callback, errback)
{
var xhr = new createXHR();
xhr.open("POST", url, true);
//xhr.setRequestHeader("Content-type", "multipart/form-data");
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
callback(parseJson(xhr.responseText));
}else{
errback("Error: "+xhr.responseText);
}
}
};
xhr.timeout = 1000000;
xhr.ontimeout = errback;
xhr.send(data);
}
Here is my Controller dealing with the request. attachment is a model and the query string binds to it no problem. I could not find out how to add a File parameter to the model, or if that would even matter. Things I have tried are under this code.
// POST: /api/db/attach
[Route("/api/[controller]/attach")]
[HttpPost]
public async Task<dynamic> attach(Attachment attachment)
{
//get the file somehow
}
i have tried many things, but cannot remember exactly what, here is one thing I did try though, which did not work.
var file = Request.Form["file"];
here is the attachment model in case it helps
namespace MyModel.Models
{
public class Attachment
{
public long id { get; set; }
public string name { get; set; }
public string value { get; set; }
}
}
Don't use query parameters or FormData if you're going to use a model on the MVC side. Just don't. And to me, it's better to just get the file into a base64 string first, than to try sending the File object, itself. I've posted about how to do that, here: Convert input=file to byte array
Then, declare and format a JSON object:
var dataObj = {
file = fileByteArray[0],
id = (id == null ? -1 : id),
name = name,
value = val
};
That fileByteArray[0] is referencing the object from my link. My answer there assumes you were just going to keep loading file base64 strings into that global array object. You can either keep it as an array, like I had, and loop through them one by one, replacing that [0] with [i], for example, as the indexer in a for loop, or just use a var fileByteArray = "" with that other code, make it so you don't push additional files but always just overwrite that variable, & just use that.
And a word of caution on that last parameter - don't use val if you use jQuery - it's a keyword. I only have it above because it's what you were passing to the URL parameter values.
Get rid of the queryParams in this line:
xhrAttach(REST_DATA + "/attach?" + queryParams, form, function (item) {
Change it to:
xhrAttach(REST_DATA + "/attach", form, function (item) {
Set the Content-Type, right where it's commented out, to:
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
Change what you are sending - it's no longer going to be FormData, it's the JSON object, and it needs to be stringified:
xhr.send(JSON.stringify(dataObj));
Fix your model to now include the file base64 string:
public class Attachment
{
public string file { get; set; }
public long id { get; set; }
public string name { get; set; }
public string value { get; set; }
}
Fix your POST method. 2 issues:
You can't use [HttpPost] if your class is inheriting ApiController, which you probably should be for this. It must be [System.Web.Http.HttpPost], and yes, it has to be completely spelled out, or it will assume it's [System.Web.Mvc.HttpPost] and not assign the route - you'd get a 404 - Not Found error when you try to do your POST. If you're inheriting from Controller, disregard this.
You will need a [FromBody] tag on your model if you are inheriting from ApiController:
public async Task<dynamic> attach([FromBody]Attachment attachment) { ... }
Then you get the file like this:
string base64FileString = attachment.file;
If you want to store it in a byte[] in the database, you can convert it:
byte[] bytes = System.Convert.FromBase64String(base64FileString);
And btw, I think your response handling is wrong. I would not do this:
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
callback(parseJson(xhr.responseText));
}else{
errback("Error: "+xhr.responseText);
}
}
};
This is how I would do it:
xhr.onreadystatechange = function(response){
if(xhr.readyState == 4 && xhr.status == 200){
callback(parseJson(response.target.responseText));
} else {
alert("Error: " + response.target.responseText);
}
};
Assuming that the response.target.responseText is getting the error sent back from the server-side in a way you can display. If not, sending it to a function that could parse it out would be the right choice. I don't think that xhr.responseText was correct.
I would suggest trying the following:
public async Task<dynamic> attach([FromURI]Attachment attachment, [FromBody] FormDataCollection formData)
And then the FormDataCollection should have the form data for retrieval.
Add a public get/set property called File to your Attachment model and the uploaded file should be bound to this property.
A model similar to yours:
https://github.com/aspnet/Mvc/blob/9f9dcbe6ec2e34d8a0dfae283cb5e40d8b94fdb7/test/WebSites/ModelBindingWebSite/Models/Book.cs#L8
public class Book
{
public string Name { get; set; }
public IFormFile File { get; set; }
}
Following controller has examples of different ways of model binding an uploaded file.
https://github.com/aspnet/Mvc/blob/9f9dcbe6ec2e34d8a0dfae283cb5e40d8b94fdb7/test/WebSites/ModelBindingWebSite/Controllers/FileUploadController.cs#L81
public KeyValuePair<string, FileDetails> UploadModelWithFile(Book book)
{
var file = book.File;
var reader = new StreamReader(file.OpenReadStream());
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
var fileDetails = new FileDetails
{
Filename = parsedContentDisposition.FileName,
Content = fileContent
};
return new KeyValuePair<string, FileDetails>(book.Name, fileDetails);
}
if this does not work, then I suspect your request is not in correct format.