I have been assigned to come up with a web service that receives and posts data. However, I am very new to this, and even after looking up multiple examples and trying to follow them, I have a bit of a difficult time understanding.
Referenced examples:
Link 1
Link 2
The code that I have been given as a reference for the model and controller are the following:
Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Webservice.Models.ApiModels {
public class SecondlyReading {
[Key]
public int Id { get; set; }
[Required]
public int Name { get; set; }
[Required]
public string TimeStamp { get; set; }
[Required]
public string Date { get; set; }
[Required]
}
}
Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;
using SmartDBWeb.Data;
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SmartDBWeb.Models.ApiModels;
using Microsoft.EntityFrameworkCore;
namespace Webservice.Controllers.Api {
[Route("api/[controller]")]
[Authorize]
public class WebserviceController : Controller {
private ApplicationDbContext _context;
public WebserviceController(ApplicationDbContext context) {
_context = context;
}
// GET: api/Webservice
[HttpGet]
public IEnumerable<Webservice> GetSecondlyReadings() {
return _context.Webservice.ToList();
}
// GET api/Webservice/id
[HttpGet("{id}")]
public async Task<IActionResult> GetWebservice(int id) {
var reading = await _context.Webservice.SingleOrDefaultAsync(c => c.Id == id);
if (reading == null) {
return NotFound();
}
return Ok(reading);
}
[HttpPost]
public IActionResult PostWebservice([FromBody]List<Webservice> Readings) {
if (!ModelState.IsValid) {
return BadRequest();
}
foreach (Webservice reading in Readings) {
_context.Webservice.Add(reading);
}
_context.SaveChanges();
return CreatedAtAction("GetWebservice", new { id = Readings[0].Id }, Readings[0]);
}
}
}
My main question is how the use of the above code works in general. What I have found out(might not be correct), is that the the model is the data itself and the controller links the model and view together.
Model: It is basically a table structure for the database. So, whenever you will create an object and set the values and insert the object in the database.
Controller: It is used to deal with the HTTP calls and to link your business logic with the View.
[HttpGet]
This maps to a GET request to the URL api/Webservice without any query parameter. The actions return type is a List, which means that multiple objects should be returned. In your case, when the client accesses api/Webservice, all objects within your _context.Webservice are returned.
[HttpGet("{id}")]
This maps to a GET request as well, but this time it requires a so called Query Parameter. This is an additional piece of information that your client provides to make its request more specific. E.g., they could be requesting api/Webservice?id=1 which would ask you to return the object with an id of 1.
[HttpPost]
This maps to a POST request and asks you to insert or update an object. [FromBody] tells the request processor to convert the so-called Request Body into an object of a given type. The request body is where your client will put entire objects - converted e.g. in JSON format - they want to submit to the server.
So, for the model
public class SecondlyReading {
[Key]
public int Id { get; set; }
[Required]
public int Name { get; set; }
[Required]
public string TimeStamp { get; set; }
[Required]
public string Date { get; set; }
}
This will create a datatable with Id as the primary key because you are using [Key] attribute.
private ApplicationDbContext _context;
This is used to create a database context. You might have also created the following in ApplicationDbContext class
public DbSet<SecondlyReading> WebService{get; set;}
It will create a DbSet with name WebService.
In WEB APIs, POST is used to insert new data. So, here the API
api/webservice
in POST, will be used to insert the data. You can insert the data using any CLIENT like POSTMAN or ARC. You have to set the data in body of request of the HTTP call.
The response of the API can be JSON or XML depending on your output.
I think it's better to read the basics?So that you will understand how normally Web API works
For self reading
https://www.asp.net/web-api
Start with asking yourself how Web Communication generally works. When accessing any website via your browser by typing in an address, what actually happens behind the scenes?
What I found to be very helpful was to open up a new tab in my browser and use Developer Tools (e.g. by right clicking anywhere and then clicking on "Inspect") to observe traffic by switching to the network tab. Access a website of your choice, say: wikipedia.org.
Now a bunch of stuff is going on, but you are interested in the first new entry to your network communication list that should say "www.wikipedia.org". Click on that.
You should now be looking at the "Headers" tab, specifically the request headers. There are two important fields:
Request URL : This tells the server what you want from it. It is a Resource locator, meaning that you want to access a resource from the server, say, a piece of HTML, an image or raw JSON data that you use in your application.
Request Method : This tells the server what you want to do with the resource you are trying to access. Do you want to GET it? Or do you want to PUT some resource on the server? Maybe you want to DELETE it or POST changes to this resource.
Let's go back to your source code.
What you provided above are a Model class and a Controller class.
Your model is a data structure that represents a resource within your web application. Id, Name, Timestamp, Date are attributes of this resource. Depending on your actual use case, you want to create, use, update or delete objects of this model type and decide on their attribute's values.
To allow your clients to do so, you have a controller class. It is the entry point for all web requests that "map" to :
[Route("api/[controller]")]
Map means, when the request URL of your client (remember our example "www.wikipedia.org") matches to the string you defined in your Route, this controller class is used (notice: [controller] will be replaced with the actual name of your controller class, in this case "Webservice".
Within your controller you define Actions. Depending on the Request URL and the Request Method (see above) of your client's request, your web framework decides, which action is called.
[HttpGet]
This maps to a GET request to the URL api/Webservice. The action's return type is a List, which means that multiple objects should be returned. In your case, when the client accesses api/Webservice, all objects within your _context.Webservice are returned.
[HttpGet("{id}")]
This maps to a GET request as well, but this time it requires a so called Query Parameter. This is an additional piece of information that your client provides to make its request more specific. E.g., they could be requesting api/Webservice?id=1 which would ask you to return the object with an id of 1.
[HttpPost]
This maps to a POST request and asks you to insert or update an object. [FromBody] tells the request processor to convert the so-called Request Body into an object of a given type. The request body is where your client will put entire objects - converted e.g. in JSON format - they want to submit to the server.
Now, I hope this makes your code examples a bit clearer to you. You also mentioned the View, so I will quickly explain what that is: Typically, after a request to your server, you respond with some sort of answer. In the most simple case, it is the Response Status that tells the client if everything went smooth. For a GET request, you typically return an object in the Response Body. What you return is called the view. In your example:
return Ok(reading);
converts the object that was retrieved from the database into a machine-readable format (e.g. JSON) and adds the response status "200 OK" to it. This is sent to your client.
So this should give you a good overview on how web frameworks work. I hope I could help you with this rather long read. Let me know if I can clarify anything.
Related
Out of simple curiosity I would like to Post data from my MVC app to my local database with Postman. Unfortunately, I encountered a certain obstacle and cannot figure out how to solve it.
The general idea is that I have a controller X with an Edit method accepting YViewModel as the only parameter.
XController : Controller
{
//Post
IActionResult Edit(YViewModel vm)
{
//Code
}
//Get
IActionResult Edit(int id)
{
//Code
}
}
YViewModel
{
public int Id { get; set; }
//Other fields below
}
In the Edit method with an HTTP GET Verb I am returning the respective view with the YViewModel containing all the required fields. All the fields (except Id) are assigned to form inputs.
The Id is only bound to the VM model. Binding works perfectly on the page, but I cannot compose the right Postman Request.
I know that I can try localhost..../X/Edit with form-data and then assign all the form fields. But where do I fit Id in that request?
Under the Body tab select x-www-form-urlencoded. For Key use Id. For Value use whatever you want to test with.
I assume you would want to post other YViewModel fields for testing.
Ensure you are creating a POST request.
If Id as the Key doesn't work you can load the page in your browser and look for the name attribute of the Id hidden field. Then use that in Postman.
I've got a .NET Core application that has a controller named Documents with a POST signature like this:
[HttpPost]
public async Task<IActionResult> PostAsync(CreateDocumentRequest createDocumentRequest)
The CreateDocumentRequest looks like this:
public class CreateDocumentRequest
{
public string Name { get; set; }
public string Description { get; set; }
public IFormFile File { get; set; }
}
Pretty simple. I then have a POST request configured in Postman like this:
URL: http://localhost:9090/api/documents
Body: configured as form-data and I have Name, Description and File all configured in the key-value pair interface. Furthermore, File is set as a file type so it allowed me to browse for a file.
When executing this POST the DocumentsController executes the constructor and Application Insights indicates that PostAsync was matched:
Activated Event Time Duration Thread
Application Insights: Trace "Route matched with {action = "PostAsync", controller = "Documents"}. Executing action TdlLims.MediaService.Controller.DocumentsController.PostAsync (TdlLims.MediaService)"
However, it never enters the action. My gut tells me that model binding is failing. This is for two reasons. One, all other pieces of the routing work according the Application Insights. Two, if I remove the parameters entirely, it does enter the action. What I've tried:
Added [FromForm] to the createDocumentRequest
Accepted only an IFormFile into the action, dropping the complex object
Split up the CreateDocumentRequest into three different parameters
And some other things along the way with less signifigance
Now, I'm suspect that when we're setting up Mvc, we may be missing something. We are configuring a few things, but I feel like we're missing a formatter for multipart/form-data somehow. I feel that way because we're using AddMvcCore instead of AddMvc:
.AddAuthorization()
.AddJsonFormatters()
.AddApiExplorer()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new OptionConverter());
options.SerializerSettings.Converters.Add(new StringEnumConverter());
});
Finally, I can confirm the controller is working in general, because I have a GET that is accessible and it makes it to the action:
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
In the end, the issue was the size of the file. It would be nice if .NET Core threw an error rather than returning a 200 when something like that happened. I was trying to upload some images, and I'm going to need to figure out the right way to increase the file size, but when I uploaded a small text file the POST worked and the file was deserialized properly into the IFormFile.
I believe the attributes RequestFormLimits and RequestSizeLimit are going to play a role in setting that max file size in the end.
I am working on tracking system for my SignalR Hub purpose. For this I have class where I want to store URL which will represent current page where user is, then his last request since I use paged list for my data tables and I need to know exactly on which page user is and of course user id.
This is my class where I want to store information
public class UserTracking
{
public string URL { get; set; }
public string LastRequest { get; set; }
public string UserId { get; set; }
}
Since I have a single page application I have problem with tracking user position on website because of angular routing, but for displaying views I have dashboard controller with ActionResults methods to allow me to display .cshtml pages with angular routing, something like this below
public ActionResult ProjectTask()
{
return View();
}
public ActionResult Project()
{
return View();
}
My question is if I am able somehow to get URL of user position on my website. For example if user is on http://localhost:2969/Dashboard#/tasks/ I want to get this /Dashboard#/tasks. Maybe I can get some information from my ActionResults, but I have no idea how.
It is hard to know if this will work for you with the information you've provided, but the following could work.
If you know the specific routes that a user must navigate to to reach you MVC Controller then you could pass in that information as a model when you return the view. Say I have an MVC route that is meant to return a Razor page that displays all the items from my list named "Freddy's Birthday". The URL in this case would look something like:
localhost/list/Freddy's%20Birthday
matching a route:
[Route("~/list/{id}")]
To inform Angular of what you're dealing with, simply pass in the information as a model:
public ActionResult List(string id)
{
return View(model: id);
}
You can access that model in your cshtml with #Model. If the id passed in was "Freddy's Birthday" like before:
<div todo-list-item-directive>#Model</div>
Would return from ASP as:
<div todo-list-item-directive>Freddy's Birthday</div>
This way works, and you could build some view models to reference in your Razor pages if you need to pass in more complicated information. However, if at all possible, it'd likely be worth your while to set up ui-router and use Angular for your view routing and ASP.NET for your API.
I use C# with VS 2015, .NET 4.5.2 and Web API 2
I want to build a restful webservice.
I need to use CURL or Fiddler for testing the webservice (not AJAX or Jquery).
I would prefer CURL if possible.
I succeeded in sending GET requests using CURL to the webservice.
But I also need to send data to the webservice using POST
Unfortunately I have problems using POST.
I need to send not only 1 parameter, but multiple paramters.
(Basically I need to send a whole record that will add to a database)
It seems that it is not possible to send multiple parameters to the POST action?
But instead of sending multiple parameters I should be able to send the record data using an array or List object ?
How should I code the POST action in the webservice controller?
How should I send the data using CURL (or fiddler) so that the POST action can process it?
I saw some examples that have used the URI to POST data... but using URI seems to need content-length to be set?
If possible I would avoid to set content-length.
Model:
The model represents the data that should be sent using POST :
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
Client:
The client (CURL/fiddler) must be able to send model data, for example:
id=5
Name="test"
Category="Hardware"
Price=10
How can I send this data using CURL (or fiddler) ?
Controller Action:
How should I code this action ?
Whould this be OK ? :
[HttpPost]
public void AddProduct([FromBody] List<Product> data )
{
Do something
}
webervice URL
Just for completeness:
My webservice URL looks like this
http://myServer/api/product
For adding such kind of thing .Make a wrapper class and then send it to POST .
[HttpPost]
public void AddProduct(List<Product> data )
{
Do something
}
Instead of this what you can do is- Make a Class
class Wrapper{
List<Product> data
}
and then use
[HttpPost]
public void AddProduct(Wrapper data )
{
Do something
}
This will work . If does't work let me know .I will try to provide you the solution . For me this worked when I was having an issue .
Assume i have this model
public partial class Todo
{
public int id { get; set; }
public string content { get; set; }
public bool done { get; set; }
}
And i send this as json data to my controller as a patch request.
This is mearly the action of toggeling a checkbox.
I think it makes sence that i only want to sent that to my server, and not the entire model.
{ "id":1, "done" : true }
What does my WebApi controller need to look like in order to correctly process this, simple, json patch request ? Should i be using web api for this, or should i use a more rpc styled approach with mvc ?
It seems like a very basic thing to do, but i can't seem to get it right !
I think i might need to use a different parameter in my controller method, but i'm not sure.
Thank you for your time.
You can find PATCH feature in the OData pre-release Nuget package: Microsoft.AspNet.WebApi.OData.
Information how you can use it to create an action for handling PATCH can be found in the Partial Updates (PATCH requests) section of the blog post about OData support in ASP.NET Web API.
Changing the method to PATCH doesn't change Web API behaviour in any way. There is no built in mechanism for doing partial updates. One of the reasons there was no PATCH method for so long is that there is no ubiquitous media type for applying patches to resources.
Secondly, you are asking Web API to do object serialization for you so there just is no such concept of applying a partially updated object. There would be so many conventions to agree on, what does a null value mean, what about an empty value, how do I say "don't update this DateTime". What about related objects, child items? How do you cause a child item to be deleted? Unless the CLR team implements some concept of a type that only contains a subset of members from another type, partial updates and object serialization are not going to go well together.
Aliostad mentions UpdateModel and that is possible when updating from a HTML form because the media type application/x-www-form-urlencoded explicitly allows for an arbitrary set of name value pairs. There is no "object serialization" going on. It is just a match of names from the form being matched to names on the Model object.
For myself, I created a new media type I use to do partial updates that works like a form but is more advanced in that it can handle hierarchial data and it maintains an order to the updates.
ASP.NET Web API seems to be missing UpdateModel, TryUpdateModel, etc.
In ASP.NET MVC, you could use them to achieve the desired effect. I have created a work item in ASP.NET Web Stack which you can vote for and if it gets enough votes, it will be implemented.
I used Microsoft.AspNet.WebApi.OData for my project and I had some problems working with JSON (working with numbers in my case). Also, the OData package has some dependencies which, from my point of view, are too big for a single feature (~7MB with all dependecies).
So I developed a simple library which do what you are asking for: SimplePatch.
How to use
Install the package using:
Install-Package SimplePatch
Then in your controller:
[HttpPatch]
public IHttpActionResult PatchOne(Delta<Todo> todo)
{
if (todo.TryGetPropertyValue(nameof(Todo.id), out int id)) {
// Entity to update (from your datasource)
var todoToPatch = Todos.FirstOrDefault(x => x.id == id);
if (todoToPatch == null) return BadRequest("Todo not found");
todo.Patch(todoToPatch);
// Now todoToPatch is updated with new values
} else {
return BadRequest();
}
return Ok();
}
The library support massive patch too:
[HttpPatch]
public IHttpActionResult PatchMultiple(DeltaCollection<Todo> todos)
{
foreach (var todo in todos)
{
if (todo.TryGetPropertyValue(nameof(Todo.id), out int id))
{
// Entity to update (from your datasource)
var entityToPatch = Todos.FirstOrDefault(x => x.id == Convert.ToInt32(id));
if (entityToPatch == null) return BadRequest("Todo not found (Id = " + id + ")");
person.Patch(entityToPatch);
}
else
{
return BadRequest("Id property not found for a todo");
}
}
return Ok();
}
If you use Entity Framework, you have to add only two lines of code after the call to the Patch method:
entity.Patch(entityToPatch);
dbContext.Entry(entityToPatch).State = EntityState.Modified;
dbContext.SaveChanges();
Furthermore, you can exclude some properties to be updated when the Patch method is called.
Global.asax or Startup.cs
DeltaConfig.Init((cfg) =>
{
cfg.ExcludeProperties<Todo>(x => x.id);
});
This is usefull when you are working with an entity and you don't want to create a model.