Map GET request to Action - asp.net-core

I'm trying to validate the progress quantity (and other fields once this works) that belongs to the BeginCollectionItems server side. The request is being sent but the parameter progressQty is not being read by the action.
This is the action I'm trying to map to:
[AllowAnonymous]
[AcceptVerbs("Get", "Post")]
public IActionResult CheckValidProgressQty(int progressQty)
{
int a =progressQty;
var result = false;
if (a > 0)
result = true;
return Json(result);
}
This is the request:
:method: GET
:path: /Components/CheckValidProgressQty?ProgressItems%5B16bad1f2-155c-4a29-844c-34e88da80b7c%5D.ProgressQty=-300
This is the Query String Parameters:
ProgressItems[16bad1f2-155c-4a29-844c-34e88da80b7c].ProgressQty: -300
Here is the remote validation in the View Model Class:
[Remote(action: "CheckValidProgressQty", controller: "Components", HttpMethod ="GET", ErrorMessage = "BAD QTY!")]
public int ProgressQty { get; set; }
Right now it goes into the CheckValidProgressQty method but I'm just not able to access the progressQty parameter. One way I can access is:
Request.QueryString.Value
?ProgressItems%5B16bad1f2-155c-4a29-844c-34e88da80b7c%5D.ProgressQty=-8
and parse it. But I think there should be something more simple available.

ProgressItems[16bad1f2-155c-4a29-844c-34e88da80b7c].ProgressQty: -300
This is posted form data when you do POST method not for GET method.
You could not get the query string on your action parameters using ?ProgressItems%5B16bad1f2-155c-4a29-844c-34e88da80b7c%5D.ProgressQty=-300since they are not match.
Refer to my below demo which introduces how to pass querystring to action, assume that I have models:
public class TestUser
{
[Key]
public int Id { set; get; }
public string Name { get; set; }
public IList<UserInterest> Interests
{
get; set;
}
}
public class UserInterest
{
[Key]
public int Id { set; get; }
[Required]
public string InterestText { set; get; }
public int Option { set; get; }
}
You need to use an object like
public ActionResult UserTest(TestUser model)
And the querystring is ?Interests[0].InterestText=hello

Related

Why does my Web Api PUT using Entity Framework 6 keep writing new duplicate records rather than updating them?

I created my database in Entity Framework, and I also created a Web Api that uses Entity Framework. When I perform a GET or a POST (ADD) everything works great, but When I do a PUT (Update) my record is not updated, it is added as if I performed a Post. I think that the following does not recognize that the Entity has been modified:
db.Entry(contact).State = EntityState.Modified;
So, here is my entire Entity Contact.cs created by Entity Framework:
public partial class Contact
{
public int Contact_ID { get; set; }
public int Dataset_ID { get; set; }
public string Booth_UCID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
public string Title_Role { get; set; }
public int Contact_Type_ID { get; set; }
public string Email { get; set; }
public string Phone_Number { get; set; }
public string Email_2 { get; set; }
public string Phone_Number_2 { get; set; }
public virtual Contact_Type Contact_Type { get; set; }
public virtual Dataset Dataset { get; set; }
}
Here is the Contact model from my application that is being sent to the Web Api:
public class Contact
{
public int Contact_ID { get; set; }
public int Dataset_ID { get; set; }
public string Booth_UCID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
public string Title_Role { get; set; }
public int Contact_Type_ID { get; set; }
public string Email { get; set; }
public string Phone_Number { get; set; }
public string Email_2 { get; set; }
public string Phone_Number_2 { get; set; }
}
And here is my MVC Application to Edit Contact
[HttpPost]
public ActionResult EditContact(Contact contact)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:4251/");
//HTTP POST
// var postTask = client.PostAsJsonAsync<Dataset>("api/datasets/1", dataset);
var postTask = client.PostAsJsonAsync("api/contacts/2", contact);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
ModelState.AddModelError(string.Empty, "Server Error. Please contact administrator.");
return View(contact);
}
and lastly, here is my Web Api with the Entity Framework scafolding: this is straight out of the box, when I created my Web Api
// PUT: api/Contacts/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutContact(int id, Contact contact)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != contact.Contact_ID)
{
return BadRequest();
}
db.Entry(contact).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ContactExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
I am at a loss as to what I could possible do. I feel like I should just abandon the Web Api with Entity Framework and just go ahead build an Empty Web Api where I control the update. And if so, how will this be different?
*** Update ***
I fixed this problem and I hope this helps others.
My issue was not within the Web Api or Entity Framework. My issue was in the Request that I was sending to the Web Api.
I wanted to do an Update (PUT), but when I ran this in debug I noticed the PUT method in my Web Api was not being triggered. I put a breakpoint on my POST method and that one was. So, I did a little research and I realized that I need to change the request below:
this line does a POST ADD, which is why I was duplicating my records in the database
var postTask = client.PostAsJsonAsync("api/datasets/2", dataset);
I changed it to the follow to do the Update:
var postTask = client.PutAsJsonAsync<Dataset>("api/datasets/2", dataset);
I thought that the uri I was sending would dictate which method put or post.

JsonProperty(Order = X)] is not working with [FromBody] and [FromRoute]

In the serialized version, It returns Request as last object even though I have ordered it first. Is there a way to set Request order 1?
Is there anything like, FromBody will always be in the end?
public class Class1
{
[FromRoute(Name = "runId")]
[JsonProperty(Order = 2)]
public string Id { get; set; }
[FromBody]
[JsonProperty(Order = 1)]
public Request Request { get; set; }
}
Model Binding order of Class1 is decided by the order of properties in the model,not by [FromRoute] or[FromBody].So if you want to bind [FromBody] first,you can do like this.Here is a demo:
public class Class1
{
[FromBody]
public Sample Sample { get; set; }
[FromRoute(Name = "runId")]
public int Id { get; set; }
}
public class Sample
{
public int Foo { get; set; }
public string Name { get; set; }
}
Controller:
[HttpPost("Create/{runId}")]
public IActionResult Create(Class1 partner) {
return Ok();
}
result:

Can not get list in a model which I get it AsQuerable in ef?

public class Product
{
public int Id{get;set;}
public int UserId{get;set;}
public Users User{get;set;}
}
I have set the Users to Product's relative:
b.HasOne("User").WithMany().HasForeignKey("UserID");
When I use entityframework to get the list of products.
The User is returned null, why?
There is a value in User table and the UserId is right in Product Table.
var list = _context.Products.AsQueryable();
the items in list has the User=null.
You need to Include the entity you're looking for. For example, let's suppose I have the following context.
AppDbContext.cs
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Notification> Notifications { get; set; }
public DbSet<Offer> Offers { get; set; }
}
Notification.cs
public class Notification
{
public int Id { get; set; }
public string Title { get; set; }
public int? OfferId { get; set; }
public virtual Offer Offer { get; set; }
}
If you want to use the Offer entity from Notification, you need to use the following statement:
context.Notifications.Include(n=> n.Offers).ToList();
// Your code goes here
In your situation:
var list = _context.Products.Include(p=> p.User).AsQueryable();
You have to explicitly ask to include the users in the returned list.
_context.Products.Include(p => p.Users).AsQueryable();

Model Binding in Web API for .NET Core Type Mismatch

I have the following controller which is supposed to create a new object in the database:
[HttpPost]
public ActionResult<Panels> CreateNewPanel(Panels panel)
{
_context.Panels.Add(panel);
_context.SaveChanges();
return CreatedAtAction(nameof(GetPanelById), new { id = panel.ID }, panel);
}
It is receiving some JSON data, example:
{
"desc": "test5",
"frame": 2,
"aC240v": false
}
Which maps to the following model:
public class Panels
{
public int ID { get; set; }
public string Desc { get; set; }
public PanelFrames Frame { get; set; }
public bool AC240v { get; set; }
}
It works for the most part if "frame" isn't set, but if it is set to an integer like the code above it fails because it is type PanelFrames not an integer.
PanelFrames is another model that has a one to many relationship with Panels, each Panel can have only one PanelFrame so in the database this is recorded as simply an integer, the PanelFrames ID.
How do I reconcile this so that the integer (which is the PanelFrame ID) get's passed through the API and recorded in the database. The MS documentation doesn't seem to cover this, though it seems like it would be a pretty common occurrence, so I must not be understanding something, or doing something very wrong.
If you use EF Core one-to-many relationships and save the principle entity(PanelFrames) id,you just need to add a foreign key for your navigation property in your Panel model.Refer to my below demo:
1.Models
public class Panels
{
[Key]
public int ID { get; set; }
public string Desc { get; set; }
public int FrameID { get; set; }
[ForeignKey("FrameID")]
public PanelFrames Frame { get; set; }
public bool AC240v { get; set; }
}
public class PanelFrames
{
[Key]
public int PanelFramesID { get; set; }
public string Name { get; set; }
public List<Panels> Panels { get; set; }
}
2.In my case, I pass json data using postman, so I need to use [FromBody] on action parameters.
json:
{
"desc": "test5",
"frameid": 2,
"aC240v": false
}
Action:
[HttpPost]
public ActionResult<Panels> CreateNewPanel([FromBody]Panels panel)
Then a new Panel with FrameId would be added into database.
3.If you need to get panels with their Frame, just use Include method in action like
using Microsoft.EntityFrameworkCore;//Add necessary namespaces before
//...
var panels= _context.Panels
.Include(p => p.Frame)
.ToList();

MVC4 Mailgun delivered webhook

I'm having an issue making use of the Mailgun delivered webhook, it can be found here: http://documentation.mailgun.net/user_manual.html#events-webhooks, look for "Delivered Event Webhook"
I am unable to reference Request.Params["Message-Id"] unless I modify the app's requestValidationMode to 2.0
I do get the potentially unsafe error when trying to reference this field without requestValidationMode = 2.0. The contents of the field are: <20130203200110.12345.12345#mydomain.mailgun.org>. I've also tried to declare a model to take advantage of auto model binding. My model looks like this:
public class MailgunDeliveredEvent
{
public string Id { get; set; }
public string Event { get; set; }
public string Recipient { get; set; }
public string Domain { get; set; }
[AllowHtml]
[JsonProperty(PropertyName="Message-Id")]
public object MessageId { get; set; }
public int Timestamp { get; set; }
public string Token { get; set; }
public string Signature { get; set; }
}
When I attempt to reference the MessageId field it returns null. I've tried to add
[Bind(Exclude="message-headers")]
As I'm not interested in that field.
In the Controller, I've set
[ValidateInput(false)]
I can't seem to get the Message-Id field back. Any help?
I seem to have got it working, in case anyone runs into the same issue...
I added a new model binder as referenced here:
Asp.Net MVC 2 - Bind a model's property to a different named value
I then changed my model like so:
[ModelBinder(typeof(DefaultModelBinderEx))]
public class MailgunDeliveredEvent
{
public string Id { get; set; }
public string Event { get; set; }
public string Recipient { get; set; }
public string Domain { get; set; }
[BindAlias("Message-Id")]
public string MessageId { get; set; }
public int Timestamp { get; set; }
public string Token { get; set; }
public string Signature { get; set; }
}
And all seems to work, I didn't need to call
[ValidateInput(false)]
on the controller either.
Hope that helps someone.