I've been going through this blog and I understand what he is saying, especially regarding the hierarchical structure (walking back along the path).
So
/objects/1/property
Removing property should give you the object with id 1, and removing the id should give you all the objects. Excellent and logical.
But I always expose my data via view models, so,
/objects/list/1 will give me the object list view model of the object with id 1. Or /objects/detail/1 will give me the object detail view model for the object with id 1.
Using this approach I have ended up with a long structure just to get a specific view model! I.e. objects/visualization/analysis/thread. Is this even restful? What I seem to be doing (subconsciously!) is structuring my restful API to match the namespace or module for where this specific view model lives (so in .NET it will be namespace: app.models.object.visualization.analysis).
How best to structure a restful endpoint like this? Is it better to have something like
objects-list/1 and objects-detail/1?
Thanks.
Example:
Sorry, I should have been more clear. I will give a .NET example. Suppose I have a cart class
public class Cart
{
public int CardId { get; set; }
public string CartName { get; set; }
public DateTime Created { get; set; }
public DateTime LastUpdated { get; set; }
public IEnumerable<CartItem> Items { get; set; }
}
With a restful design, I could expose carts as /carts, /carts/1, /carts/1/items and so on. But I always expose view models, not the actual data layer object. I.e.
public class CartListModel
{
public int CartId { get; set; }
public string CartName { get; set; }
}
and
public class CartViewModel
{
public int CartId { get; set; }
public string CartName { get; set; }
public DateTime LastUpdated { get; set; }
public IEnumerable<CartItemViewModel> Items { get; set; }
}
So this way I am only exposing the data that I actually need for a specific purpose. Now at the moment, I am exposing these view models as such /carts/list or /carts/list/1. Also /carts/view and /carts/view/1. So the original question is this restful? Do I infact need a separate endpoint for each view model? So /carts-list and /carts-view, carts-view/1 etc.
Non .NET example
Don't really know what to put here! A view model is a representation of the object, only exposing certain properties necessary to bind to a view.
So suppose my object has the following JSON structure
{
id: 1,
name: 'Cart 1',
lastUpdated: '26-Sep-2014 16:51:23',
items: [
// an array of objects
]
}
For a certain view, like a simple table, I may only need the id and the name. So I expose a restful endpoint that gives me back the following structure
{
id: 1,
name: 'Cart 1'
}
Everything else is unnecessary. For a cart edit page, I will probably need a lot more data than just the id and name. The question is, how do I structure a restful endpoint to expose these different representations of the same object?
URIs are stable
Resources are identified by URIs. The get the object with ID 1, do
GET /objects/1
To get a list of all objects, just
GET /objects
Use content negotiation
What representation of the object 1 is returned by the server is decided by content negotiation. This is done using HTTP headers, not URL path segments or query parameters. Do this:
GET /objects/1
Accept: appliction/vnd.com.example.object.detail+json
By this the client could request something you call the "detail view model".
If the client wants to get the "list view model", you could do
GET /objects/1
Accept: appliction/vnd.com.example.object.list+json
Note
The URL is the same for both requests.
The Accept headers have different values.
Don't use different URIs
Don't do any of these:
GET /objects/1/list: This would request the sub resource called list from object 1.
GET /objects/1/list: This would request another sub resource.
GET /objects/1?model=detail or GET /objects/1?model=list: These are different URIs which identify different resources.
Just try to keep your URL as simple as possible. That means that if your API was a house and you want all clothes of a person named Marie, your URL would be:
API/persons/Marie/clothes
And not:
API/house/persons/Marie/clothes/all
Sorry for the bad examples. Rest is pretty hard to explain.
Related
I'm building a .NET WebAPI that receives Json through a Post operation. The Json that's being received could look like the following:
{
"site": "00131231201d010231",
"publishTime": 123123123123,
"domains": [
"example.com"
],
"publishedBy": {
"name": "John Doe",
"id": "00211231201d010231"
}
}
I converted my Json response type to C# objects which look like the following:
public class Project
{
[Key]
[JsonPropertyName("site")]
public string Site { get; set; }
[JsonPropertyName("publishTime")]
public long PublishTime { get; set; }
[JsonPropertyName("domains")]
public List<Domain> Domains { get; set; }
[JsonPropertyName("publishedBy")]
public PublishedBy PublishedBy { get; set; }
}
public class PublishedBy
{
[JsonPropertyName("name")]
public string Name { get; set; }
[Key]
[JsonPropertyName("id")]
public string Id { get; set; }
}
public class Domain
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
}
As you can see, my goal is to add the contents to my database. Only when I use List Domains, it gives me an error saying I can't use strings in EFCore when I try to add a migration.
So, I created an object called Domain. But now when I try to deserialize it gives me the following error:
System.Text.Json.JsonException: The JSON value could not be converted to spine_management.Models.Domain.
Does anyone happen to know what type I should make Domains and/or what the best way to deserialize this object is?
EDIT:
I want to keep the domains attribute, I don't want to ignore or delete them.
It's not uncommon to split between two different model structures for interacting with different infrastructure points. In this case your infrastructure points are:
Deserializing JSON input
Persisting data with EF
You can treat the JSON input like a "view model". It's not your core model which maps to EF database entities, but rather just an anemic DTO for deserializing data. For that model, Domains is simply a list of strings:
[JsonPropertyName("domains")]
public List<string> Domains { get; set; }
This view model is local to the application layer, not part of the core domain. Within the application logic, after deserializing the input, you can map it to the domain object. That's where you would translate the list of simple strings into a list of Domain objects. (And translate back in any output operations.)
As long as the mapping logic (which might be made simple by using tools like AutoMapper, though in this case the logic is pretty straightforward and doesn't really necessitate adding more tools) is encapsulated within that application layer, it won't pollute the rest of the domain logic.
Though it may certainly be possible to configure one or both of these tools to work together more smoothly, I often find that a simple translation layer between dependency-specific DTOs and core domain models is much simpler to build and maintain.
I currently use Newtonsoft JSON for serialize and deserializing. I think the reason for this error is you wrote named the string in domain "Name", but it has to be "name". Hope it works!
I'm facing little stranger issue with Web API controller. I have a collection which is being passed in an action of api controller. Object being used is collection is having 4 properties.
My action is able to accept collection parameter when it's properties are in specific order. See below :-
[HttpPost]
public ForexRates UpdateRates([FromBody] Rates rates)
{
// TODO: Obviously code :)
return rates;
}
This code is being place in API controller & calling from Postman. See below:-
<rates>
<rate>
<id>fefef</id>
<rate>35353.333</rate>
<series>dfefge</series>
<series-order>sfefefef</series-order>
</rate></rates>
If I change the order of the properties I started getting null value in my action. Can some one please explain this :)
Models
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Rate { get; set; }
}
public class Rates : Collection<ForexRate>
{
}
You will need to control the order with which your XML is serialized. Use XmlElementAttribute and specify the Order.
There is a similar question here
FYI, I suppose there is no way for you to change the order of the properties, while you supply from PostMan to your WebApi service. You will need to follow the exact order.
If you don't wanna do that, then pass this Xml as a string parameter and then parse it inside a method.
The default binder can have issues when the same name is used in different places during binding.
In your case you've got Rate.Rate - both class name and property name. Try changing your class to (and corresponding xml for the post) :
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Value { get; set; }
}
and then try changing the order.
While I don't have a definitive reason why it works in one order and not another, it's likely that when it gets to the Rate(double) value it tries to create a new Rate(object) but doesn't have the correct properties (as its just a double).
A more complicated solution would be to write a specific model binder for the Rate object.
The issue has to do with the DataContractSerializer which expects the elements to occur in a specific order (alphabetical with some consideration given to inheritance). That's the default serializer used when creating a Web API project.
You can override this and specify a different serializer during API Configuration like this:
GlobalConfiguration.Configuration.Formatters.XmlFormatter
.SetSerializer<SomeType>(new XmlSerializer(typeof(SomeType)));
I am trying to build a helper class for Datatables and need some advice.
I want to create a helper that I can pass in an object that represents the settings for a DataTables that is typed by a specific model. This object would contain a collection of expressions that represent the columns to be used by the helper. As part of the DataTables object there will be a property that holds the URL that servers up the data for the grid. This will effectively be the sAjaxSource parameter. As part of the URL call, a JSON package will be sent that will contains the information about the columns. This whole section, I understand how to build. Basically, I would build out the DataTables JavaScript code including column definitions and create a JSON object that represents the columns I want to pass in the URL.
The area I needs some advice on is building the data for the specific columns once hte call is made to the server / controller. So my controller may look like this:
public ActionResult GetUsersList(IEnumerable<DatatableColumnJson> columns)
{
var users = _someUserRepository.GetAll();
foreach (var user in users)
{
//Here I would build the JSON that DataTables needs to render the grid.
//I would iterate through the users and pull out the data for the specific columns
}
return Json(someCompleteDataTablesObject, JsonRequestBehavior.AllowGet);
}
Supporting Classes:
public class DatatableColumnJson
{
public string Name { get; set; }
public string Visible { get; set; }
}
public class User
{
public string UserName { get; set; }
public string Email { get; set; }
}
So I first thought I could use reflection, but that seems to be a little intensive, maybe. Another thought was to transform the column names into expressions to possibly pull out the data. I want something efficient and that doesn't have a lot of overhead, but maybe I am asking too much.
I'm struggling with the design aspect of building this web site, its my first website and I'm not sure of the correct direction i need to take this project in. I've posted on this previously but not done a good job of explaining it. So I'll attempt to do so now.
The Site will be used for submitting "Innovation Ideas" from employees. It will have to connect up to an already existing MS Access Database. There will be two tables in this database that it has to communicate with.
The first one Is the InnovationSubmission Table which which looks similar to this :-
ID(Auto Generated),
Short Description,
Long Description,
Name,
Email Address,
Date(From Date.Time.Now()) - on Submission.
Team Name
Area (Area of Business)
The User will use a Web Form(View) to enter the details above, it will be validated, then saved to the back end database. I have this working in a fashion. The issue has started when I have tried to introduce two DropDownlistsFor contorls based on another table which is below :-
AREA A - Team1, Team3, Team5, Team7
AREA B - Team2, Team4, Team6, Team8
This is just sample Data, there are only two different areas in the table, but there will be over 50 teams split across them. I will be looking to have the Teams dropdownList filter on the value in the Area DropDownlist.
In my Models folder I have created a InnovationSubmission Class, that replicates the table in the database, this class is used as a strongly typed data type in the View representing the Submission Form. Its how i Validate the User input and I pass this class to a c# method that sends the data back using ADO.NET.
I'm struggling with how I should be trying to implement the dropdownlists.
Will I need to create a class similar to the InnovationSubmission Class, to represent the Teams/ Area Table?
Is the table at present structured in the best way for this project?
Can I populate both dropdownlists from the 1 table?
How do I relate the Teams & Area Columns?
Any Help would be greatly appreciated!?!
Would this be the correct way to design my View Model :-
public class MyViewModel
{
public int ID { get; set; }
public string shortDesc { get; set; }
public string longDesc { get; set; }
public string Status { get; set; }
public string originator { get; set; }
public string originatorEmail { get; set; }
public IEnumerable<Area> area { get; set; }
public IEnumerable<Team> team { get; set; }
}
public class Team
{
public string teamName { get; set; }
}
public class Area
{
public string area { get; set; }
}
You seem to be talking about cascading dropdown lists where the values of the second one update based on the selection made in the first one. I have illustrated an example of how this could be achieved in this post. The idea is that you subscribe to the .change event of the first dropdownlist and then trigger an AJAX request to a controller action passing the selected value in which you wold query the database and return a list of possible options.
Ok I've been trying to figure out the best way to do this for a few days, but still haven't come up with a very elegant answer so am hoping I someone can point me in the right direction or give some peer review :)
Basically I have 3 classes (they are different and much more complex than these):
public class Person
{
int ID { get; set;}
string Name { get; set; }
virtual IEnumerable<Place> { get; set; }
}
public class Place
{
int ID { get; set;}
string Name { get; set; }
virtual IEnumerable<Thing> { get; set; }
}
public class Thing
{
int ID { get; set;}
string Name { get; set; }
virtual IEnumerable<Place> { get; set; }
virtual int PersonID { get; set; }
}
So basically you have Persons, who have many Places, which can have many Things which can also appear in multiple Places (trying to reduce having to store duplicates of Things) but only for that Person
What is the best way to setup my ViewModel to handle this? Should I just create everything by itself using Ajax and Json (what I've been doing) or is there a way to handle this type of relationship in a ViewModel and single post back to the server?
Currently I'm doing the following:
Fill out Person form -> ajax save to server, get Person ID
Fill out Place form (including Person's ID) -> ajax save to server, get Place ID
Fill out Thing form (including Person ID and Place IDs in a delimited string
I know there should be an easier way to do this as its kinda bulky, but since its all query string I can't figure it out
You say "kinda bulky," but I think it tends to be more lightweight if you can build an object graph on a form in real time by using AJAX/JSON, probably against a RESTful API, somewhat as you describe.
The alternative is using script (jQuery, JSMVC, Knockout) to build a form and POST the whole sucker at once. I've had to do this in some situations where none of the data should be persisted until the whole graph is committed. The trick here is understanding ModelBinder and how it builds/updates that graph for you.
If this is what you were asking, I can expand on the key points of how ModelBinder deals with complex object graphs and collections.
I answered a similar question about how to handle this using interfaces and partial views.
How to create Asp.Net MVC 3 view model