How to post JSON data to SQL using ajax post & knockout - asp.net

I have a pretty straightforward view model:
var ProjectViewModel = {
ProjectName: ko.observable().extend({ required: "" }),
ProjectDescription: ko.observable().extend({ required: "" }),
ProjectStartDate: ko.observable(),
ProjectEndDate: ko.observable()
};
I want to save this data that is located in my viewmodel to my SQL server.
I have a class defining this View Model in my Server Side Code:
public class Projects
{
public string ProjectName { get; set; }
public DateTime ProjectStartDate { get; set; }
public DateTime ProjectEndDate { get; set; }
public string ProjectDescription { get; set; }
}
I also have this web method to receive the code:
[WebMethod]
public bool SaveProject(string[] JSONDATA)
{
TaskNinjaEntities entities = new TaskNinjaEntities();
foreach (var item in JSONDATA)
{
Console.WriteLine("{0}", item);
}
return true;
}
And finally I have this POST that does not want to send the data to the server:
function SaveMe() {
var data = ko.toJSON(ProjectViewModel);
$.post("CreateProject.aspx/SaveProject", data, function (returnedData) {
});
}
I get nothing from the returned data in this post method, also added breakpoint in server side code, and it doesn't hit it at all. My URL is correct and the Viewmodel converts to JSON without hassle.

Make the web method static.
[WebMethod]
public static bool SaveProject(string[] JSONDATA)
{
TaskNinjaEntities entities = new TaskNinjaEntities();
foreach (var item in JSONDATA)
{
Console.WriteLine("{0}", item);
}
return true;
}

Related

System.Text.Json Deserialize Fails

With this DTO:
public class QuestionDTO {
public Guid Id { get; set; }
public string Prompt { get; set; }
public List<Answer> Choices { get; set; }
public QuestionDTO() {
}
public QuestionDTO(Question question) {
this.Id = question.Id;
this.Prompt = question.Prompt;
this.Choices = question.Choices;
}
}
I was getting an error about Unable to Parse without a parameterless constructor. I have since fixed that, but now my objects are de-serialized empty:
using System.Text.Json;
var results = JsonSerializer.Deserialize<List<QuestionDTO>>(jsonString);
The jsonString contains 3 items with the correct data, and the deserialized list contains 3 items, but all the properties are empty.
The new json library is case sensitive by default. You can change this by providing a settings option. Here is a sample:
private JsonSerializerOptions _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
private async Task SampleRequest()
{
var result = await HttpClient.GetStreamAsync(QueryHelpers.AddQueryString(queryString, queryParams));
_expenses = await JsonSerializer.DeserializeAsync<List<Common.Dtos.Expenses.Models.Querys.ExpensesItem>>(result, _options);
}

asp.net put an empty C# list to razor view and fill it up inside

I want to put an empty C# List to the razor view. Inside of the view I get array of data (taken from JavaScript code). Fill the content of the array into the C# List and put it back to the controller.
Now, I know that is not possible, then I find a more simpler solution, see details in my example.
One Item of the collection
public class GrafikSpielItem
{
public String Stil { get; set; }
public int XStart { get; set; }
public int YStart { get; set; }
public int XStop { get; set; }
public int YStop { get; set; }
public String Farbe { get; set; }
}
controller class
// GET: GrafikSpiel
[HttpGet]
public ActionResult AufgabeB(string[] arr)
{
// arr is the JSON-String from Grafik.js
if (arr == null) return View();
List<GrafikSpielItem> items = new List<GrafikSpielItem>();
// adapted from: https://stackoverflow.com/questions/19910476/c-sharp-parsing-json-array-of-objects
// Thanks to: Bibaswann Bandyopadhyay
JArray array = JArray.Parse(arr[0]);
foreach (JObject obj in array.Children<JObject>())
{
var item = new GrafikSpielItem();
int nCounter = 1;
foreach (JProperty singleProp in obj.Properties())
{
switch(nCounter)
{
case 1: item.Stil = singleProp.Value.ToString(); break;
// case 2: ..
default:
break;
}
nCounter++;
}
items.Add(item);
}
// not implemented yet
// connect to database
// db.SetData(items)
return RedirectToAction("Index");
}
Razor view
...
// moved source code from here to JS
<script src="~/Scripts/Grafik.js"></script>
</body>
</html>
JavaScript: Grafik.js
// convert simple array to JSON and send it back to controller
var arrStr = encodeURIComponent(JSON.stringify(linesArray));
var url = "AufgabeB?arr=" + arrStr;
window.location.href = url;
I found a solution - see edited code - thanks to user mortb and user Bibaswann Bandyopadhyay.

Combine [FromBody] with [FromHeader] in WebAPI in .net Core 3.0

we are writing some API which required sessionId in header and some other data in body.
Is it possible to have only one class automatically parsed partially from header and from body?
Something like:
[HttpGet("messages")]
[Produces("application/json")]
[Consumes("application/json")]
[Authorize(Policy = nameof(SessionHeaderKeyHandler))]
public async Task<ActionResult<MessageData>> GetPendingClockInMessages(PendingMessagesData pendingMessagesRequest)
{
some body...
}
with request class like:
public class PendingMessagesData
{
[FromHeader]
public string SessionId { get; set; }
[FromBody]
public string OrderBy { get; set; }
}
I know, it is possible to do this, but it means, that I have to pass SessionId into the other methods as a parameter, instead of pass only one object. And we would have to do that in every API call.
public async Task<ActionResult<MessageData>> GetPendingClockInMessages(
[FromHeader] string sessionId,
[FromBody] PendingMessagesData pendingMessagesRequest)
{
some body...
}
Thank you,
Jakub
we are writing some API which required sessionId in header and some other data in body. Is it possible to have only one class automatically parsed partially from header and from body
Your GetPendingClockInMessages is annotated with a [HttpGet("messages")]. However, a HTTP GET method has no body at all. Also, it can't consume application/json. Please change it to HttpPost("messages")
Typically, SessionId is not passed in header of Session: {SessionId} like other HTTP headers. Session are encrypted via IDataProtector. In other words, you can't get it by Request.Headers["SessionId"].
Apart from the above two facts, you can create a custom model binder to do that.
Since the Session doesn't come from header directly, let's create a custom [FromSession] attribute to replace your [FromHeader]
public class FromSessionAttribute : Attribute, IBindingSourceMetadata
{
public static readonly BindingSource Instance = new BindingSource("FromSession", "FromSession Binding Source", true, true);
public BindingSource BindingSource { get { return FromSessionAttribute.Instance; } }
}
And since you're consuming application/json, let's create a binder as below:
public class MyModelBinder : IModelBinder
{
private readonly JsonOptions jsonOptions;
public MyModelBinder(IOptions<JsonOptions> jsonOptions)
{
this.jsonOptions = jsonOptions.Value;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var type = bindingContext.ModelType;
var pis = type.GetProperties();
var result= Activator.CreateInstance(type);
var body= bindingContext.ActionContext.HttpContext.Request.Body;
var stream = new System.IO.StreamReader(body);
var json = await stream.ReadToEndAsync();
try{
result = JsonSerializer.Deserialize(json, type, this.jsonOptions.JsonSerializerOptions);
} catch(Exception){
// in case we want to pass string directly. if you don't need this feature, remove this branch
if(pis.Count()==2){
var prop = pis
.Where(pi => pi.PropertyType == typeof(string) )
.Where(pi => !pi.GetCustomAttributesData().Any(ca => ca.AttributeType == typeof(FromSessionAttribute)))
.FirstOrDefault();
if(prop != null){
prop.SetValue( result ,json.Trim('"'));
}
} else{
bindingContext.ModelState.AddModelError("", $"cannot deserialize from body");
return;
}
}
var sessionId = bindingContext.HttpContext.Session.Id;
if (string.IsNullOrEmpty(sessionId)) {
bindingContext.ModelState.AddModelError("sessionId", $"cannot get SessionId From Session");
return;
} else {
var props = pis.Where(pi => {
var attributes = pi.GetCustomAttributesData();
return attributes.Any( ca => ca.AttributeType == typeof(FromSessionAttribute));
});
foreach(var prop in props) {
prop.SetValue(result, sessionId);
}
bindingContext.Result = ModelBindingResult.Success(result);
}
}
}
How to use
Decorate the property with a FromSession to indicate that we want to get the property via HttpContext.Sessino.Id:
public class PendingMessagesData
{
[FromBody]
public string OrderBy { get; set; } // or a complex model: `public MySub Sub{ get; set; }`
[FromSession]
public string SessionId { get; set; }
}
Finally, add a modelbinder on the action method parameter:
[HttpPost("messages")]
[Produces("application/json")]
[Consumes("application/json")]
public async Task<ActionResult> GetPendingClockInMessages([ModelBinder(typeof(MyModelBinder))]PendingMessagesData pendingMessagesRequest)
{
return Json(pendingMessagesRequest);
}
Personally, I would prefer another way, i.e, creating a FromSessionBinderProvider so that I can implement this without too much effort. :
public class FromSessionDataModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var sessionId = bindingContext.HttpContext.Session.Id;
if (string.IsNullOrEmpty(sessionId)) {
bindingContext.ModelState.AddModelError(sessionId, $"cannot get SessionId From Session");
} else {
bindingContext.Result = ModelBindingResult.Success(sessionId);
}
return Task.CompletedTask;
}
}
public class FromSessionBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
var hasFromSessionAttribute = context.BindingInfo?.BindingSource == FromSessionAttribute.Instance;
return hasFromSessionAttribute ?
new BinderTypeModelBinder(typeof(FromSessionDataModelBinder)) :
null;
}
}
(if you're able to remove the [ApiController] attribute, this way is more easier).

Web Api POST for inserting data with Complex type is always null

I'm quite new to Web-Api concepts. Trying to insert data using the basic project provided by Microsoft (Adding a simple Test Client to ASP.NET Web API Help Page).
http://blogs.msdn.com/b/yaohuang1/archive/2012/12/02/adding-a-simple-test-client-to-asp-net-web-api-help-page.aspx
Below is the code for Inserting data into Headoffice table.(HeadofficeID int ,HeadofficeName varchar(50),Notes varchar(1000),Isactive bit)
[POST("create")]
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
{
if (_headOfficeBLL.Insert(headOffices) > 0)
return Request.CreateResponse(HttpStatusCode.Created, headOffices);
else
return Request.CreateResponse(HttpStatusCode.BadRequest, headOffices);
}
Headofficemodel Class
public partial class HeadOfficeModel : AtlAuthenticationModelBase
{
public int HeadOfficeId { get; set; }
public string HeadOfficeName { get; set; }
public string Notes { get; set; }
public bool IsActive { get; set; }
}
In the front end when i try to send the data from URI or Body only null values are getting inserting. While debugging all i can see in Headoffice model is null values.Below are the different ways i tried to insert data
1) {"HeadOfficeName":"TestHO1", "Notes":"notes", "IsActive":true}
2) {"TestHO1", "notes", true}
3) ={"headOffices":{"HeadOfficeName":"TestHO1","Notes":"notes","IsActive":false}}
and also tried to change the code as below
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
public HttpResponseMessage Post([FromBody]HeadOfficeModel headOffices)
public HttpResponseMessage Post([ModelBinder]HeadOfficeModel headOffices)
Been trying to fix this from two days. When i send the data as complex type its not working else as separate parameters (changing the method to accept parameters) its working fine
public int Post(string Name, string Notes, bool Active)
{
HeadOfficeModel objHOM = new HeadOfficeModel();
objHOM.HeadofficeName = Name;
objHOM.Notes = Notes;
objHOM.IsActive = Active;
return _headOfficeBLL.Insert(objHOM);
}
Below is the html code where i m hiting while inserting
<script>
testClientModel = {
HttpMethod: '#Model.ApiDescription.HttpMethod',
UriPathTemplate: #Html.Raw(Json.Encode(Model.ApiDescription.RelativePath)),
UriParameters: [
#foreach (var parameter in Model.ApiDescription.ParameterDescriptions)
{
if (parameter.Source == System.Web.Http.Description.ApiParameterSource.FromUri)
{
#:{ name: "#parameter.Name", value: "" },
}
}
],
Samples: {
#Html.Raw(#String.Join(",", Model.SampleRequests.Select(s => String.Format("\"{0}\": \"{1}\"", s.Key, HttpUtility.UrlEncode(s.Value.ToString()))).ToArray()))
},
BaseAddress: '#applicationPath'
};
</script>
Can you please help me where am i going wrong. Attaching screenshot.
Entered both in URI and Body just to show that i tried different ways.
enter image description here

How to add the UserId to posted data by Logged User in ASP.NET MVC 4

So what I'm doing might seem simple, but I don't know exactly how to do it.
I have already registered and logged in with an account (I'm using the default membership system used in ASP.NET MVC 4) and so I want to do add my UserId to some data I'm inserting to the database.
This is the model of the data I'm inserting:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Reroute.Models
{
public class Request
{
public int RequestId { get; set; }
// I want to add UserId based on my current session
public int UserId { get; set; }
public string OrderNumber { get; set; }
public string TrackingNumber { get; set; }
public string CurrentAddress { get; set; }
public string NewAddress { get; set; }
public string Comment { get; set; }
}
}
And the ActionResult (here's where I supposed I have to make the changes):
[HttpPost]
public ActionResult Create(Request collection)
{
try
{
_db.Requests.Add(collection);
_db.SaveChanges();
//return RedirectToAction("Index");
return Content("Done! Added to DB");
}
catch
{
return View();
}
}
Thanks
use this it gets u the userid ...
Membership.GetUser().ProviderUserKey
You can save the UserId of the authenticated user in Session after logging in:
Session["UserId"] = userId;
or since you are using FormsAuthentication you can either use the UserData property as shown here or do a nice-that-will-do-trick:
public SignInUser(string name, string id) {
// store the userid
FormsAuthentication.SetAuthCookie(name + '|' + id, false);
}
then retrieve the Name and UserId like this:
public int CurrentUserId
{
get
{
var context = HttpContext.Current;
if (context == null) return 0;
return context.Request.IsAuthenticated
? Convert.ToInt32(context.User.Identity.Name.Split('|')[1])
: 0;
}
}
public string CurrentUserName
{
get
{
var context = HttpContext.Current;
if (context == null) return string.Empty;
return context.Request.IsAuthenticated
? context.User.Identity.Name.Split('|')[0]
: string.Empty;
}
}
You can have those method and properties in a class so you have them in one place, I actually do it that way. Now, you can call it in your controller like so:
[HttpPost]
public ActionResult Create(Request collection)
{
try
{
collection.UserId = _authProvider.CurrentUserId;
// if you want to use session, I prefer the FormsAuthentication approach
// you need to do additional check that the Session has not expired (not null)
collection.UserId = Session["UserId"];
_db.Requests.Add(collection);
_db.SaveChanges();
//return RedirectToAction("Index");
return Content("Done! Added to DB");
}
catch
{
return View();
}
}
_authProvider is an instance of the class that has the code I gave above.
This should work.
var loggedInUserName=Thread.CurrentPrincipal.Identity.Name;
var user=Membership.GetUser(loggedInUserName);
var key = user.ProviderUserKey;
T
Assuming your Create also has a GET which is loaded up and used as the model for Create.cshtml, you would just need to set it explicitly in that ActionResult
public ActionResult Create()
{
Result model = new Result();
model.UserId = myUserId;
}
Then in your Create.cshtml you could have a hidden field for it:
#Html.HiddenFor(m => m.UserId)
I would still check in the POST to make sure the user doing the saving is allowed to be saving and hasn't spoofed your hidden field value to somebody completely different.

Resources