Asp.net mvc5 same action POST and GET in the same method - asp.net

Can i send to the action method new record and then get the new id by the same method
public class HomeController : Controller
{
[HttpGet]
[HttpPost]
public JsonResult _sendConfirmation'(string subject,string mail)
{
Some Code--Some Code---Some Code
return Json(new { Success = true, id = newCreatedMailId });
}
}
Getting the id by jquery
$.getJSON('/Mails/_sendConfirmation', function (comingData) {
alert("success" + data);
jQuery.get('/Mails/_getNewMailSendConfirmation', { id: comingData }, function (data) {
jQuery('#myModal').modal('show');
jQuery('#myModal .modal-body').html(data);
});
enter code here

So your Ajax request is looking for an action named _sendConfirmation on your Mails controller and not finding it but you've chose. To just show us your home controller index action so we have no idea if the appropriate controller exists or not with that.
As far as having it decorated with a get and a post, when you do this you are essentially telling the action to look for data on the request in two different places which will most likely not end well for you unless you plan on doing that switch logic yourself inside the action. Your code would probably be a bit less error prone if you seperated the action and once extracted the data, you call the same helper methods to work your data.

Related

How to Update Model in ASP NET MVC 6?

Scenario: How to update a model?
ASP MVC 6
I am trying to update a model. For passing the model information to the client(browser/app) I am using the DTO.
Question 1: For updating, should I post the whole object back?
Question 2: Is there a way I can easily pass only the information that is updated? If yes, how?
Question 3: Can I use JSON Patch for updation?
Question 2: Is there a way I can easily pass only the information that
is updated? If yes, how?
Yes. You should create a view model which should have only those properties needed for the view.
Let's assume your use case is to build a view which allows user to edit only their last name.
public class EditUserViewModel
{
public int Id {set;get;}
public string LastName {set;get;}
}
And in your Get
public ActionResult Edit(int id)
{
var user = yourUserRepository.GetUser(id);
if(user!=null)
{
var v = new EditUserViewModel { Id=id,LastName=user.LastName};
return View(v);
}
return View("NotFound");
}
And the view
#model EditUserViewModel
#using(Html.BeginForm())
{
#Html.TextBoxFor(s=>S.LastName)
#Html.HiddenFor(s=>s.Id)
<input type="submit" id="saveBtn" />
}
and your HttpPost action
[HttpPost]
public ActionResult Edit(EditUserViewModel model)
{
// Since you know you want to update the LastName only,
// read model.LastName and use that
var existingUser = yourUserRepository.GetUser(model.Id);
existingUser.LastName = model.LastName;
yourUserRepository.Save();
// TO DO: redirect to success page
}
Assuming yourUserRepository is an object of your data access classes abstraction.
Question 1: For updating, should I post the whole object back?
Depends on what you want from the end user. With this view model approach, It is going to post only the Id and LastName and that is our use case.
Can I use JSON Patch for updating?
Since you are only sending the data which needs to be updated (partial data), you should be fine.
If you want,you may simply serialize your form data(which has only Id and LastName) and use jQuery post method to send it to your server.
$(function(){
$("#saveBtn").click(function(e){
e.preventDefault(); //prevent default form submit
var _form=$(this).closest("form");
$.post(_form.attr("action"),_form.serialize(),function(res){
//do something with the response.
});
});
});
To prevent overposting, you can use a binding whitelist using Bind attribute on your HttpPost action method. But the safest strategy is to use a view model class that exactly matches what the client is allowed to send.
Instead of this
UpdateModel(model);
You now can call this
await TryUpdateModelAsync(model);

Front end ASP.NET MVC4 as one project, and an ASP.NET Web API as another project in same solution - how to call WebAPI from front end?

I have an ASP.NET MVC4 front end as one project in my solution, and a separate ASP.NET Web API as another project in the same solution. The Web API will contain all of my CRUD operations.
2 questions
How do I call my Web API from my front end to perform CRUD operations? I have my entity data model defined in my Web API project, and I will need to bind my front end views to it, how would I do that?
Once this is deployed to my web servers, the front end will reside on one server, and the Web API will reside on another server (the server that holds most of our web services). So, I guess along the same lines, how would I call the Web API from my front end once deployed? I understand Web API's are simply called with an HTTP request, but in terms of passing my models (which are defined in my Web API project) into my Views (in my front end project), how can I do this?
While Kevin is right, I did this the non-Ajax way. Keep in mind that I am working with JSON data, so this is centered around JSON.
In your controller page, remove anything that has to do with DbContext, Entity Framework, etc. The reason is by default, the controller will want to perform CRUD operations by calling the DbContext, and we don't want this. We want to call the WebAPI instead to do this.
First and foremost, declare some member variables in your controller. The rest of your controller will utilize these:
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
Uri contactUri = null;
In your controller, create a constructor for your controller, as such:
public ContactController()
{
// set base address of WebAPI depending on your current environment
client.BaseAddress = new Uri("http://server/YourAPI/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
Replace the Index action's code with something like the following. Note that the only relevant pieces are the client.GetAsync() call and the var contacts assignment. Everything else is not necessary for the context of this problem. The value inside the client.GetAsync() should be the name of your controller, prepended by any custom routing you set up in your WebApiConfig.cs - in my case, I added the api part in my route to distinguish between API calls and normal calls:
public ActionResult Index()
{
response = client.GetAsync("api/contact").Result;
if (response.IsSuccessStatusCode)
{
var contacts = response.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
return View(contacts);
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Create action (the HttpPost action) with something like the following. Again, the only important piece is the client.PostAsJsonAsync() part - this is what calls the WebAPI's POST action which takes care of, in my case, inserting a new record into the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Contact contact)
{
// Create a new product
response = client.PostAsJsonAsync("api/contact", contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Edit action (the non-HttpPost action) with something like the following. This was a little tricky because in order to edit, you had to retrieve the record first, so basically, the HttpPost version of Edit will contain somewhat similar code, with an additional line of code that performs the edit POST (PUT). Below, we're getting the response from the WebAPI by passing it a specific record ID. So, just like for Index (GET), we are doing the same thing only passing in the ID so we only get back one record. Then, we cast the response to an actual object that can be operated on in the View:
public ActionResult Edit(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Replace the Edit action (the HttpPost action) with something like the following. Below, we're getting the record to be edited by calling client.GetAsync() and passing in the primary key as a parameter (contact_id). Then, we're getting the RequestUri from that response and saving it. Then, we're calling client.PutAsJsonAsync() and passing in the Uri.PathAndQuery (what we just saved) as well as the object to be edited.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Contact contact)
{
response = client.GetAsync(string.Format("api/contact/{0}", contact.contact_id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.PutAsJsonAsync(contactUri.PathAndQuery, contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Delete action (the non-HttpPost action) with something like the following. So again, we're getting the record from the database by simply calling client.GetAsync() and casting it to an actual object my app knows of.
public ActionResult Delete(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Finally, replace the Delete action (the HttpPost action) with something like the following. Again, we're doing something similar to that of the Edit action. We are getting the record to be deleted, casting it to an object, and then passing that object into a client.DeleteAsync() call, as shown below.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.DeleteAsync(contactUri).Result;
return RedirectToAction("Index");
}
You can call your Web API from the client using jQuery ajax method. But since you are calling from a site other than where the Web API is deployed you will have to use JSONP, instead of JSON. Take a look at this QA to see how you use JSONP with Web API. Your models will be passed as JSON which you will have to render on the client side, instead of using Razor to render it on the server side. I would use something like Knockout to create a View Model on the client that will bind your model to the HTML elements on the client.

Is it possible to use $.post with RedirectToAction in mvc

I'm new to asp.net, Im posting all my data to server using $.post (Jquery syntax).
Action of controller is executed # the end I'm calling RedirectToAction to different action method, after completion of action method execution.
Eecution reaches to completed callback event of $.Post() where im loading result of request in html to root element of page. $(html).html(results).
How can i use $.post with RedirectToAction
What's the point of using AJAX when you replace the whole page? The whole point of AJAX is to refresh only a certain portion of the DOM. If you are going to refresh your entire page, then simply use a standard link, no need of AJAX. But if we suppose that your controller action handles 2 cases: one in which it returns a partial view and one in which it redirects, you could pass the target url as JSON:
[HttpPost]
public ActionResult SomeAction()
{
if (Something)
{
return PartialView();
}
return Json(new { redirectTo = Url.Action("Foo", "Bar") });
}
and then on the client:
$.post('#Url.Action("SomeAction")', function(result) {
if (result.redirectTo) {
// the controller action passed us the url to redirect to
window.location.href = result.redirectTo;
} else {
// the controller action passed us a partial result =>
// let's update some portion of the DOM
$('#someId').html(result);
}
});

How do I return a 404 status where invalid parameters are passed to my ASP.NET MVC controller?

I want to return a HTTP status 404 if invalid arguments are passed to my controller. For example if I have a controller that looks like:
public ActionResult GetAccount(int id)
{
...
}
Then I want to return a 404 if say urls such as these are encountered:
/GetAccount
/GetAccount/notanumber
i.e. I want to trap the ArgumentException that is thrown.
I know I could use a nullable type:
public ActionResult GetAccount(int? id)
{
if(id == null) throw new HttpException(404, "Not found");
}
But that's pretty icky and repetitious.
I was hoping I could add this to my controllers where necessary:
[HandleError(View="Error404", ExceptionType = typeof(ArgumentException))]
public class AccountsController : Controller
{
public ActionResult GetAccount(int id)
{
...
}
}
But that doesn't appear to work well.
I saw this post and this answer which nearly solves my problem:
In that answer an abstract BaseController is created from which you derive all your other controllers from:
public abstract class MyController : Controller
{
#region Http404 handling
protected override void HandleUnknownAction(string actionName)
{
// If controller is ErrorController dont 'nest' exceptions
if (this.GetType() != typeof(ErrorController))
this.InvokeHttp404(HttpContext);
}
public ActionResult InvokeHttp404(HttpContextBase httpContext)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error");
errorRoute.Values.Add("action", "Http404");
errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
errorController.Execute(new RequestContext(
httpContext, errorRoute));
return new EmptyResult();
}
#endregion
}
This works great at handling unknown actions with a 404 but doesn't allow me to handle invalid data as a 404.
Can I safely override Controller.OnException(ExceptionContext filterContext) like this:
protected override void OnException(ExceptionContext filterContext)
{
if(filterContext.Exception.GetType() == typeof(ArgumentException))
{
filterContext.ExceptionHandled = true;
this.InvokeHttp404(filterContext.HttpContext);
}
else
{
base.OnException(filterContext);
}
}
On the surface it seems to work, but am I storing up any problems by doing this?
Is this semantically correct thing to do?
Best way? Action method selector attribute!
To actually avoid nullable method arguments I suggest that you write an Action Method Selector attribute that will actually only match your action method when id is supplied. It won't say that argument wasn't supplied but that it couldn't match any action methods for the given request.
I would call this action selector RequireRouteValuesAttribute and would work this way:
[RequireRouteValues("id")]
public ActionResult GetAccount(int id)
{
...
}
Why is this the best solution for your problem?
If you look at your code you'd like to return a 404 on actions that match name but parameter binding failed (either because it wasn't supplied or any other reason). Your action so to speak requires particular action parameter otherwise a 404 is returned.
So when adding action selector attribute adds the requirement on the action so it has to match name (this is given by MVC) and also require particular action parameters. Whenever id is not supplied this action is not matched. If there's another action that does match is not the issue here because that particular action will get executed. The main thing is accomplished. Action doesn't match for invalid route request and a 404 is returned instead.
There's an app code for that!
Check my blog post that implements this kind of attribute that you can use out of the box. It does exactly what you're after: it won't match your action method if route data provided doesn't have all required values.
Disclaimer: this does not cover all the cases
For urls in your examples, returning 404 can be done in single line. Just add route constraint for id parameter.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index" }, // Parameter defaults
new { id = #"\d+" } // restrict id to be required and numeric
);
And that's all. Now any matching url that has no id or id is not numeric, autimatically triggers not found error (for which there are plenty of ways to handle, one in your example, another by using custom HandleErrorAttribute, etc). And you can use non-nullable int parameters on your actions.
I managed to get this working by adding this route at the end of all routes:
routes.MapRoute("CatchAllErrors", "{*url}",
new { controller = "Error", action = "NotFound" }
);
Note: First I followed this: How can I properly handle 404 in ASP.NET MVC?

How to get these values front end using asp.net mvc

Date:
model.Date) %>--%> // Should I use this as Input type?
Number#:
Comment
I am trying to get these three fields on the screen while user enters I am retreving the user enter data on front end.. when I am debugging I am not seeing these fields..
On the view I am using beginForm
<% using (Html.BeginForm("Update", "Home", FormMethod.Post, new { #id = "id" }))
{ %>
my method..
public JsonResult Update(StudentInfo info)
{
///Update
return Json(Status.ToString());
}
when I see in info I am not getting these three fields..
can any one help me out thanks
You are returning a JsonResult but doing as Http post (Html.BeginForm). If you want to use a full form post then return a ActionResult.
public ActionResult Index()
{
// Add action logic here
return View();
}
you can call void controller
It makes no sense that you're returning a JsonResult from a HTML Post.
Do this instead.
[HttpPost]
public ActionResult Update(StudentInfo info)
{
///Update
if (updateWorked)
return View("Success", status);
}
You use JsonResult when you want to call a controller that returns JSON data, in order to display this data somewhere on your page.
A useful scenario for JsonResult in your scenario would be returning a json list of Students, executed from a click event maybe (call in JavaScript/jQuery).
Calling an action method on a HTTP Post which returns a JsonResult of a single string (not real JSON) makes no sense.

Resources