I implemented an odata controller that creates an entity on a POST request.
public virtual IHttpActionResult Post(TEntity e)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_service.Insert(e);
return Created(e);
}
I want to create a successor/copy of an existing entity. So I load the existing entity via GET, strip out some data (e.g. the ID) show it in my UI and send it via POST. But I want to copy some additional objects / do some more stuff if the entity is a successor.
Do I have to use an odata action or can I pass additional parameters like POST: odata/Entity?successorOf=12345? How can I read this parameters? Or is there a better odata-way of doing this?
Related
The application was built on a bunch of asp .net core mvc and entity framework.
I have a map with markers on it. I want to change the parameters of a certain object through textboxes. The request from the frontend is written in axios, and it works flawlessly. From the first time I get the changes in the database. (mysql, provider: pomelo.mysql).
When I try to access the get request for the first time, I get the old state of the object.
HttpGet request is described here:
public async Task<IEnumerable<Poi>> GetPois()
{
var pois = await _poiService.GetPois();
if (pois.Status == Domain.Enum.StatusCode.Ok)
{
return pois.Data;
}
else { return null; }
}
I have an interface that describes the necessary set of manipulations with the Poi object.
IPoiService is described here:
public interface IPoiService
{
Task<BaseResponse<IEnumerable<Poi>>> GetPois();
Task<BaseResponse<Poi>> GetPoi();
Task<BaseResponse<bool>> DeletePoi();
Task<BaseResponse<Poi>> CreatePoi();
Task<BaseResponse<Poi>> UpdatePoi(Poi entity);
}
The service for working with the Poi object is described here:
public async Task<BaseResponse<IEnumerable<Poi>>> GetPois()
{
try
{
return new BaseResponse<IEnumerable<Poi>>
{
Data = await _poiRepository.GetAll().ToListAsync(),
Status = Domain.Enum.StatusCode.Ok
};
}
catch(Exception ex)
{
return new BaseResponse<IEnumerable<Poi>>
{
Status = Domain.Enum.StatusCode.InternalServerError,
Description = $"[GetPois]: {ex.Message}"
};
}
}
BaseResponse and the corresponding interface represents the response from the database, so it doesn't affect the update problem in any way.
I also have a repository that directly implements instance operations at the database level.
The repository is described here:
public class PoiRepository : IBaseRepository<Poi>
{
private readonly ApplicationDbContext db;
public PoiRepository(ApplicationDbContext db)
{
this.db = db;
db.Database.OpenConnection();
}
public Task Create(Poi entity)
{
throw new NotImplementedException();
}
public Task Delete(Poi entity)
{
throw new NotImplementedException();
}
public IQueryable<Poi> GetAll()
{
return db.Pois;
}
public Poi Update(Poi entity)
{
db.Pois.Update(entity);
db.SaveChanges();
return entity;
}
}
Thus, I get the problem that in order to get the current data, I need to perform two HttpGet requests, and only after that EF Core will return its current value to me.
The reason that Update(entity) sends off warning bells is that you are passing entities between server and client and back. When a controller returns a View(entity) you are sending a reference entity to the view engine to build the view. The view's #Model allows you to apply bindings but it is not a client-side copy of the entity. However, when your form submit or Ajax call etc. calls back with the #model that is NOT an entity, let alone the entity the view engine was given. It will only be a copy of data and only as complete as the view bindings could populate.
So it's hard to deduce what exactly you are witnessing without stepping through the application, but my gut says you are most likely getting confused by what you think is passing entity references around. Think of it this way, in your POST actions you could accept a set of ints, strings, and such for each of the values of the model, or a completely different class definition (DTO/ViewModel) with the same fields as the entity. ASP.Net would attempt to fill in using the data submitted with a Form POST or Ajax call. By accepting an "Entity" you are just telling EF to populate the data into a new untracked entity class. It's not the same instance as a DbContext originally loaded, and the DbContext is a different instance (or should be) than when the entity was originally loaded, it isn't tracking the entity that was originally loaded.
The resulting object will only contain the details that the view happened to have stored in the individual bound controls, pieced back together behind the scenes.
My recommendation is simply to never pass entities to, and especially from a view. Use an explicit ViewModel to represent the state sent to and from a view, then in your Update method:
Fetch the actual entity using the ViewModel ID,
Check a concurrency token (RowVersionNumber / Timestamp) to ensure no changes were made to the DB since you originally fetched the data to populate the View. (optional, but recommended)
Validate the data in your view model
Copy the data from the view model into the Entity. (Automapper can help here)
SaveChanges()
No use of Update or Attach in the DbContext/DbSet.
Allow me to explain in more detail.
I've been learning and testing around with ASP.NET's MVC 5 using Visual Studio 2017. From what I understand, a Controller's "Actions" or methods are mapped according to a route format in "RouteConfig.cs", making all public methods accessible to web requests.
In the case of a simple GET method that returns a View, like this one:
// GET: Movies/Create
public ActionResult Create()
{
return View();
}
I would only need to enter the correct URL and I have obtained the View.
But in the case of sensible POST actions like deleting a data-entry, how does the controller make sure that the POST request is a valid one that comes from one of its own Views, instead of an unknown webpage? With the assumption that an action only need to be mapped to a matching route to be called.
Using a code sourced from one of Microsoft's tutorials as an example:
public class MoviesController : Controller
{
private MovieDBContext db = new MovieDBContext();
/*
Bunch of Other GET Actions
*/
// POST: Movies/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Movie movie = db.Movies.Find(id);
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
/*
Bunch of Other GET Actions
*/
}
How does this Controller achieve this? If not, how can it be achieved?
That's the purpose of the Anti-Forgery token, which you're validating by decorating the action method with the ValidateAntiForgeryToken attribute. Your view will need to include an anti-forgery token to be validated via the #Html.AntiForgeryToken() HtmlHelper method
Based on your example, ValidateAntiForgeryToken will do this job.
If a bit more explanation, MVC has its own disciplined that once you create a new controller which name is "MyTest", naming convention is MyTestController. It means if you create a view for MyTest controller, a folder with MyTest should be created under View and where MyTest's views are supposed to be kept.
I hope you would get my explanation.
Enjoy coding !
I am testing a push notification on a calendar application. When I create an event on calendar application, my website gets a HttpPost request with a JSON string. I wrote code like this but I couldn't receive the JSON string in my action method.
[HttpPost]
public ActionResult Push(String jsonReq)
{
Console.write(jsonReq);
return View();
}
When I create model in the same structure as JSON, then I can receive the request. it seems to be tightly coupled to JSON structure ? I am using in ASP.Net MVC 4.
[HttpPost]
public ActionResult Push(JSONModel jsonModel)
{
return View();
}
ASP.NET MVC model binding works the following way - it parses the request, tries to find a name-to-name corresponding between its parameters and Action paramaters, and if found instantiates latter. You are not sending parameter with name jsonReq, so you cannot receive something in your action method.
If you really want to work with plan json string without letting ASP.NET MVC parse it for your, you have two options:
Access it via HttpContext.Request inside the action
Write custom model binder that will map the request body to the jsonReq parameter
The request would not have a value named jsonReq so would not know to map the json to that action parameter.
Where as your JSONModel will have property names that match the JSON named values coming into the request thus the object us populated.
TL;DR Summary: Can I configure MVC Web API routing for HTTP GET, PUT & DELETE?
I've been looking into replacing our old Data Access Layer (a DLL based on DataSets and TableAdapters) with a private API, with a view to creating a public API if it's successful. I've done some work with MVC 4 to refresh our frontend, and loved working with it, so it seems sensible to explore the "Web API" project type before diving into WS- or WCF-based libraries.
An initial demo allows me to return XML/JSON nicely, for example:
//service.url/api/Users
... returns a list of users, while a specific user's details can be accessed via:
//service.url/api/Users/99
So far, so RESTful. However, in order to truly map URIs to resources I want to do an HTTP PUT (new user) or HTTP DELETE (remove user) to the the URI listed above. In all of the examples I've seen for these projects, along with the Scaffolds provided in Visual Studio, this convention is followed:
//service.url/api/Users/Create
//service.url/api/Users/Delete/99
//service.url/api/Users/Update/99
... and so on. This feels like side-stepping the issue to me, which is a shame when what's there has been put together so nicely!
Any thoughts on how best to approach this?
What you want is the default in MVC Web API. I'm not sure what you are looking at but here is a great example of routing the Get/Post/Put/Delete to actions.
For example you may want:
public class UsersController : ApiController
{
// GET http://service.url/api/Users/1
[HttpGet]
public User GetUser(int id);
// POST http://service.url/api/Users/?name=richard...
[HttpPost]
public User AddUser(User model);
// PUT http://service.url/api/Users/?id=1&name=Richard...
[HttpPut]
public User UpdateUser(User model);
// DELETE http://service.url/api/Users/1
[HttpDelete]
public User DeleteUser(int id);
}
I've explicitly set these, but the GetUser and DeleteUser don't need the prefix because they start with the matching HTTP method.
The link provided by Erik is a good start, but I see how it can confuse the situation when looking for a simple RESTful API that makes use of the HTTP verbs to perform these CRUD actions. If you're looking to use the HTTP verbs of GET, PUT, POST, and DELETE (and possibly PATCH, but I'm not covering that here) and you're ok with using convention, then the following would work:
public class UsersController : ApiController
{
// GET http://service.url/api/Users
public User GetAllUsers(){ ... }
// GET http://service.url/api/Users/1
public User GetUser(int id){ ... }
// POST http://service.url/api/Users/
// User model is passed in body of HTTP Request
public User PostUser([FromBody]User model){ ... }
// PUT http://service.url/api/Users/1
// User model is passed in body of HTTP Request
public User PutUser(int id, [FromBody]User model){ ... }
// DELETE http://service.url/api/Users/1
public User DeleteUser(int id){ ... }
}
Note that the attributes on the method are not needed when using the HTTP verb action convention in Web API. Also, note that I use the [FromBody] attribute on the User parameter for POST and PUT to denote that the body contains the data I wish to send. This may not be most convenient for POST if you're trying to append to a resource, and I have not tried creating/modifying data through query parameters using Web API. It certainly makes the call feel very clean to place your data in the body. "POST/PUT this content in the body at this resource."
Also, the way I read PUT in the spec, and I could very well be wrong, is that it acts as a replace. That also makes sense given the last line above. I'm PUTting this resource in this location, replacing what was already there. The spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) states: "If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server." The term they use is "modified" so I guess that leaves enough room for interpretation for the end user. That's where PATCH comes in (https://www.rfc-editor.org/rfc/rfc5789), but I don't have enough information to comment on that at this time.
I am using ASP.NET and MVC 3 in visual studio and have a question about passing an object from one action method(called Search) to another(called SearchResult). I tried using ViewData but it didnt persist down to the View of SearchResult. Below is a snippet of my code, from within a single controller. 'Search' gets called when data has been collected from a form and submit has been called(hence the [HttpPost] declaration):
[HttpPost]
public ActionResult Search(Search_q Q){
// do some work here, come up with an object of type 'Search_a'
// called 'search_answer'.
ViewData["search_answer"] = search_answer;
return RedirectToAction("SearchResults");
}
public ActionResult SearchResult(Search_a answer)
{
return View(answer);
}
I also tried using RedirectToAction("SearchResults", new {answer = search_answer}); instead of the above call to RedirectToAction but my 'search_answer' still didnt persist to the View. What is the best way to send this Search_a object to the SearchResult View?
You can use TempData to pass the object.
[HttpPost]
public ActionResult Search(Search_q Q){
// do some work here, come up with an object of type 'Search_a'
// called 'search_answer'.
TempData["search_answer"] = search_answer;
return RedirectToAction("SearchResult");
}
public ActionResult SearchResult()
{
var answer = (Search_a)TempData["search_answer"];
return View(answer);
}
If TempData does not suffice (what Eranga suggested), because SearchResult is not somewhat dependent on a Redirect (though you can have checks to get around it), you might want to look at the Request object. You can likely store the data as one of the query parameters via Request.Params. This can take advantage of the ModelBinding filter chain that Asp MVC has.