Post data with free-jqgrid, what are the code in client and Web API side? - asp.net

I am using ASP.NET MVC 6 Web API. In the View I use free-jqgrid.
Let's borrow Oleg's free jqgrid data to demonstrate my purpose. We already have the table shown.
Next I am going to add new Vendor. Please notify that there is primary key id(identity column) in the database. We don't want it displaying in the screen.
In VendorRespository.cs, I add the new Vendor as
public void AddVendor(Vendor item)
{
using (VendorDataContext dataContext = new VendorDataContext())
{
dataContext.Database.Connection.ConnectionString = DBUtility.GetSharedConnectionString(
"http://centralized.admin.test.com");
var newVendor = dataContext.Vendors.Create();
newVendor.Company = item.Company;
newVendor.ContactName = item.ContactName;
newVendor.ContactPhone = item.ContactName;
newVendor.UserName = item.UserName;
newVendor.UserKey = item.UserKey;
newVendor.Active = item.Active;
newVendor.FacilityId =item.FacilityId;
newVendor.ClientID = item.ClientID;
dataContext.SaveChanges();
}
}
My questions:
Not sure the script like?
<script>
API_URL = "/VendorManagement/";
function updateDialog(action) {
return {
url: API_URL
, closeAfterAdd: true
, closeAfterEdit: true
, afterShowForm: function (formId) { }
, modal: true
, onclickSubmit: function (params) {
var list = $("#jqgrid");
var selectedRow = list.getGridParam("selrow");
rowData = list.getRowData(selectedRow);
params.url += rowData.Id;
params.mtype = action;
}
, width: "300"
};
}
jQuery("#jqgrid").jqGrid('navGrid',
{ add: true, edit: true, del: true },
updateDialog('PUT'),
updateDialog('POST'),
updateDialog('DELETE')
);
In the controller, not sure what is the code?
// POST
public HttpResponseMessage PostVendor(Vendor item)
{
_vendorRespository.AddVendor(item);
var response = Request.CreateResponse<Vendor>(HttpStatusCode.Created, item);
string uri = Url.Link("DefaultApi", new { id = item.Id });
response.Headers.Location = new Uri(uri);
return response;
}
My code has many compiling errors such as
'HttpRequest' does not contain a definition for 'CreateResponse' and the best extension method overload 'HttpRequestMessageExtensions.CreateResponse(HttpRequestMessage, HttpStatusCode, Vendor)' requires a receiver of type 'HttpRequestMessage'
Please help me to get rid of the error and inappropriate code.
EDIT:
I borrowed the code snippet from here.

I need add the code such as
[Microsoft.AspNet.Mvc.HttpGet]
public dynamic GetVendorById(int pkey)
{
return null;
}
And
// POST
[System.Web.Http.HttpPost]
public HttpResponseMessage PostVendor(Vendor item)
{
_vendorRespository.AddVendor(item);
var response = Request.CreateResponse<Vendor>(HttpStatusCode.Created, item);
string uri = Url.Link("/VendorManagement/GetVendorById", new { id = item.pkey });
response.Headers.Location = new Uri(uri);
return response;
}

Related

Web API translate JSON object into simple parameters

If I am sending JSON data (via POST) to a .Net Core Web API like this
{ a: "a", b: "b" }
What do I need to do to have a controller method like this?
[HttpPost]
public async Task SometMethod(string a, string b)
{
return Ok();
}
Normally, all tutorials and docs say that you need to define a class and use [FromBody] attribute. But how can I make do without extra classes that I don't really need?
Firstly,your json should be:
{
"a":"a",
"b":"b"
}
You could receive data as JObject instead of a class like below:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpPost]
public void Post(JObject data)
{
//get the property value like below
var data1 = data["a"].ToString();
var data2 = data["b"].ToString();
}
}
Result (For easily distinguish value and property name,I change a to aaa and b to bbb):
If you want to post the data to the method like this, you will have to serialize your data before you can send it to the server. Assuming you are using JQuery, you can do like the following.
var postData = $.param({ a: "a", b: "b" });
//Then you can send this postData obejct to the server. This should perfectly bound to the parameters.
You can also use the same in an angular app.
After some research I came up with ModelBinder to do just this. It is not performant since it re-parses the whole request body for every parameter. I will improve it in the future.
https://github.com/egorpavlikhin/JsonParametersModelBinder
public class JsonBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var actionDescriptor = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor.MethodInfo.GetCustomAttributes(typeof(JsonParametersAttribute), false).Length > 0)
{
var context = bindingContext.HttpContext;
if (context.Request.ContentType != "application/json")
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
#if (NETSTANDARD2_1 || NETCOREAPP3_0)
context?.Request.EnableBuffering();
#else
context?.Request.EnableRewind();
#endif
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8,
false,
1024,
true); // so body can be re-read next time
var body = await reader.ReadToEndAsync();
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(body);
if (json.TryGetValue(bindingContext.FieldName, out var value))
{
if (bindingContext.ModelType == typeof(string))
{
bindingContext.Result = ModelBindingResult.Success(value.GetString());
}
else if (bindingContext.ModelType == typeof(object))
{
var serializerOptions = new JsonSerializerOptions
{
Converters = {new DynamicJsonConverter()}
};
var val = JsonSerializer.Deserialize<dynamic>(value.ToString(), serializerOptions);
bindingContext.Result = ModelBindingResult.Success(val);
}
}
context.Request.Body.Position = 0; // rewind
}
}
}

my action method returning {"success=true,message="work done"} ASP.net MVC 5

Here is my create action method. I want get alert form it when success is true.
public JsonResult Create(Student student ,HttpPostedFileBase img)
{
if (ModelState.IsValid)
{
if (img !=null)
{
var name = Path.GetFileNameWithoutExtension(img.FileName);
var ext = Path.GetExtension(img.FileName);
var filename = name + DateTime.Now.ToString("ddmmyyyff") + ext;
img.SaveAs(Server.MapPath("~/img/"+filename));
student.ImageName = filename;
student.Path = "~/img/" + filename;
}
db.Students.Add(student);
db.SaveChanges();
return Json(new { success = true, responseText = "The attached file is not supported." }, JsonRequestBehavior.AllowGet);
}
ViewBag.ClassID = new SelectList(db.Classes, "Id", "Name", student.ClassID);
return new JsonResult { Data = new { success = false, message = "data not saved" } };
}
Here is my ajax function :
function onsub(form) {
$.validations.unobtrusive.parse(form);
if (form.valid()) {
var ajaxConfig = {
type: "POST",
url: form.action,
data: new FormData(form),
success: function (response) {
if (response.success ) {
alert(response.responseText);
} else {
// DoSomethingElse()
alert(response.responseText);
}
}
}
if ($(form).attr("enctype") == "multipart/form-data") {
ajaxConfig["contentType"] = false;
ajaxConfig["processData"] = false;
}
$.ajax(ajaxConfig);
}
return false;
}
How can I get an alert form it
without reloading the form. I also want to submit images and other files to create an action method.
This is the result that I get after submitting the form:
In your case you are calling Create action which returning the JSON Result and the same Json response is displayed in browser.
Their should be a View page from where you will call this method by using the Ajax call, then you will be able to see your alert message.

'Server side events' send with the ASP Web Api do not arrive?

I created a test source which should send a message to the client every x time. This is the ApiController:
public class TestSourceController : ApiController
{
private static readonly ConcurrentQueue<StreamWriter> ConnectedClients = new ConcurrentQueue<StreamWriter>();
[AllowAnonymous]
[Route("api/sources/test")]
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) OnStreamAvailable,
"text/event-stream");
return response;
}
private static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
{
var clientStream = new StreamWriter(stream);
ConnectedClients.Enqueue(clientStream);
}
private static void DoThings()
{
const string outboundMessage = "Test";
foreach (var clientStream in ConnectedClients)
{
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage));
clientStream.Flush();
}
}
}
The clientStream.Flush(); is called like expected and without exceptions.
I handle it in AngularJS like this:
$scope.handleServerCallback = function (data) {
console.log(data);
$scope.$apply(function() {
$scope.serverData = data;
});
};
$scope.listen = function () {
$scope.eventSource = new window.EventSource("http://localhost:18270/api/sources/test");
$scope.eventSource.onmessage = $scope.handleServerCallback;
$scope.eventSource.onopen = function() { console.log("Opened source"); };
$scope.eventSource.onerror = function (e) { console.error(e); };
};
$scope.listen();
My guess is it's a problem with the server since I can see the "EventStream" from the test call is empty in the chrome debugger.
Does anyone know how to make sure the messages arrive at the client?
The solution was quite easy, according to the spec every line has to end with "\n" and the very last line with "\n\n".
So:
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage) + "\n\n");
Solves it.

How can I use the ContextKeys property for the AjaxFileUpload control?

I started looking at the AjaxFileUpload control, specifically the ContextKeys property. However, I do not understand how to use it.
The documentation says of AjaxFileUpload that the ContextKeys is used to pass information to the server when a file is uploaded. But no examples are provided. Are there any examples online that I could look at?
Though such functionality not implemented (I believe it was planned but by some reasons was postponed), nothing protect you from implement it yourself. To do this you need to download AjaxControlToolkit source code and tweak it for your needs.
There will be a lot of points so you may to prepare a cup of coffee before :)
I'll show changes with name of file that must being changed.
Server/AjaxControlToolkit/AjaxFileUpload/AjaxFileUpload.cs file
First of all, add ContextKeys property to the AjaxFileUploadEventArgs.cs file (it located in same folder):
/// <summary>
/// Gets or sets the context keys.
/// </summary>
public string ContextKeys
{
get;
set;
}
After that open the AjaxFileUpload class code and change the OnPreRender method. Here is a part of this method with custom modifications:
var eventArgs = new AjaxFileUploadEventArgs(guid, AjaxFileUploadState.Success,
"Success", uploadedFile.FileName,
uploadedFile.ContentLength, uploadedFile.ContentType,
stream.ToArray());
// NEW CODE HERE
eventArgs.ContextKeys = this.Page.Request.Form["contextKeys"];
That's all changes in server code we need. Now we need to modify the Sys.Extended.UI.AjaxFileUpload client class (file AjaxFileUpload.pre.js )
Firstly let's modify _html5UploadFile method as below:
_html5UploadFile: function (fileItem) {
this._guid = Sys.Extended.UI.AjaxFileUpload.utils.generateGuid();
var uploadableFile = fileItem.get_fileInputElement();
var fd = new FormData();
fd.append("fileId", uploadableFile.id);
fd.append("Filedata", uploadableFile.file);
if (this.contextKeys) {
if (typeof this.contextKeys !== "string") {
this.contextKeys = Sys.Serialization.JavaScriptSerializer.serialize(this.contextKeys);
}
fd.append("contextKeys", this.contextKeys);
}
$common.setVisible(this._progressBar, true);
this._setDisableControls(true);
this._html5SetPercent(0);
this._setStatusMessage(String.format(Sys.Extended.UI.Resources.AjaxFileUpload_UploadingHtml5File, uploadableFile.file.name, Sys.Extended.UI.AjaxFileUpload.utils.sizeToString(uploadableFile.file.size)));
var url = this._postBackUrl;
if (url.indexOf("?") != -1)
url += "&";
else
url += "?";
this._webRequest = new Sys.Net.WebRequest();
this._executor = new Sys.Net.XMLHttpExecutor();
this._webRequest.set_url(url + 'contextkey=' + this._contextKey + '&guid=' + this._guid);
this._webRequest.set_httpVerb("POST");
this._webRequest.add_completed(this.bind(this._html5OnRequestCompleted, this));
//this._executor.add_load(this.bind(this._html5OnComplete, this));
this._executor.add_progress(this.bind(this._html5OnProgress, this));
this._executor.add_uploadAbort(this.bind(this._html5OnAbort, this));
this._executor.add_error(this.bind(this._html5OnError, this));
this._webRequest.set_executor(this._executor);
this._executor.executeRequest(fd);
}
As you can see above, we adding contextKeys to form data, posted with Ajax request.
The we need to modify the _uploadInputElement method:
_uploadInputElement: function (fileItem) {
var inputElement = fileItem.get_fileInputElement();
var uploader = this;
uploader._guid = Sys.Extended.UI.AjaxFileUpload.utils.generateGuid();
setTimeout(function () {
uploader._setStatusMessage(String.format(Sys.Extended.UI.Resources.AjaxFileUpload_UploadingInputFile, Sys.Extended.UI.AjaxFileUpload.utils.getFileName(inputElement.value)));
uploader._setDisableControls(true);
uploader.setThrobber(true);
}, 0);
var url = uploader._postBackUrl;
if (url.indexOf("?") != -1)
url += "&";
else
url += "?";
uploader._createVForm();
uploader._vForm.appendChild(inputElement);
if (this.contextKeys) {
if (typeof this.contextKeys !== "string") {
this.contextKeys = Sys.Serialization.JavaScriptSerializer.serialize(this.contextKeys);
}
var contextKeysInput = document.createElement("input");
contextKeysInput.setAttribute("type", "hidden");
contextKeysInput.setAttribute("name", "contextKeys");
contextKeysInput.setAttribute("value", this.contextKeys);
uploader._vForm.appendChild(contextKeysInput);
}
uploader._vForm.action = url + 'contextkey=' + this._contextKey + '&guid=' + this._guid;
uploader._vForm.target = uploader._iframeName;
setTimeout(function () {
uploader._vForm.submit();
uploader._waitTimer = setTimeout(function () { uploader._wait() }, 100);
}, 0);
}
After all these changes you can set ContextKeys property in code-behind and get it value from AjaxFileUploadEventArgs argument of the UploadComplete event as below:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack && !AjaxFileUpload1.IsInFileUploadPostBack)
{
AjaxFileUpload1.ContextKeys = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(new Dictionary<string, string> { { "1", "First" }, { "2", "Second" } });
}
protected void AjaxFileUpload1_OnUploadComplete(object sender, AjaxFileUploadEventArgs file)
{
if (!string.IsNullOrEmpty(file.ContextKeys))
{
var contextKeys = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Dictionary<string, string>>(file.ContextKeys);
}
Also, if you'll implement OnClientUploadStarted client-side event as proposed here link, you may pass to server your contextKeys from client:
function uploadStarted(sender, args) {
sender.contextKeys = { "first": "1", "second": "2" };
}

How to pass content in response from Exception filter in Asp.net WebAPI?

Consider following code:
My problem is:
1) I can't seem to cast the errors to HttpContent
2) I can't use the CreateContent extension method as this doesn't exist on the context.Response.Content.CreateContent
The example here only seems to provide StringContent and I'd like to be able to pass the content as a JsobObject:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
public class ServiceLayerExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
var exception = context.Exception as ModelValidationException;
if ( exception != null )
{
var modelState = new ModelStateDictionary();
modelState.AddModelError(exception.Key, exception.Description);
var errors = modelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage);
// Cannot cast errors to HttpContent??
// var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = errors};
// throw new HttpResponseException(resp);
// Cannot create response from extension method??
//context.Response.Content.CreateContent
}
else
{
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
}
}
base.OnException(context);
}
}
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
context.Response.Content = new StringContent("Hello World");
you also have the possibility to use the CreateResponse (added in RC to replace the generic HttpResponseMessage<T> class that no longer exists) method if you want to pass complex objects:
context.Response = context.Request.CreateResponse(
context.Exception.ConvertToHttpStatus(),
new MyViewModel { Foo = "bar" }
);

Resources