Web API - REST help and clarification - c#

I'm writing an API using WebAPI, and whilst I'm designing this from scratch, I'm trying to make it RESTful. In the past when doing web service work, I've always used RPC patterns (ASMX, SOAP, etc) - eg. using an arbitrary method name that matches what I want it to do. To me this seems much more intuitive and explicit than REST, but given WebApi seems to be more RESTful by nature (I know you can change the route so it's not), I've decided to try and make it RESTful.
I understand (I think!) the basics - eg. POST for create, PUT for update when specifying an ID, GET to get, DELETE to delete, etc, etc.
My confusion is how to deal with returning collections vs a single object. For example, say that I've got an API controller called UsersController. As far as I gather, I'd have the following URLs:
GET: /api/users (lists all users)
GET: /api/users/1 (lists details about user with ID 1)
POST: /api/users (creates a new user with details sent in the POST data)
PUT: /api/users/1 (updates the existing user with ID 1 with details sent in the POST data)
DELETE: /api/users/1 (deletes the existing user with ID 1)
For the first URL above, I need to also send various filter/search criteria. Does this criteria just get passed through as query string parameters? Or should I be using a completely different controller for this "search" functionality? Eg. UserSearchController? If so, should that be a POST in this case or still a GET? Actually as I write this, I'm wondering whether a separate controller makes sense anyway, as I'll probably want more detail returned in the GET for a single user than in the search results. Does it make it not RESTful if the same controller does return different data for a single object GET vs a GET returning a collection?

For the first URL above, I need to also send various filter/search criteria. Does this criteria just get passed through as query string parameters?
It definitely makes sense to use query string for specifying filter/search parameters.
should I be using a completely different controller for this "search" functionality? Eg. UserSearchController?
You should not do that. There are couple of reasons that i see here:
There will be almost identical functionality in GET: /api/users
You can easily implement GET: /api/users, GET: /api/users?filter=...&sort=... and GET: /api/users/1 in one method:
//If you are using EF it could look like
//filter and sort arguments could be added here as well
public HttpResponseMessage Get(int? id)
{
if(id.HasValue)
{
return Request.CreateResponse(
HttpStatusCode.OK,
Context.Users.SingleOrDefault<Users>(u => u.Id == id));
}
var users = Context.Users.Select(apply filter).OrderBy(apply sort).ToList();
return Request.CreateResponse(HttpStatusCode.OK, users);
}
You can take a look at OData - it may help you with the implementation.
Spreading this logic between different controllers compromises single responsibility principle - your users controller should handle all the logic related to users and only this logic
If so, should that be a POST in this case or still a GET?
If you want to make your API RESTful you should be using GET. BUT you should be aware, that returning an array of JSON objects through GET could be potentially vulnerable to JSON hijacking. One of the simplest solutions to this exploit is allowing to get JSON arrays through POST only (there are other solutions as well).
I'll probably want more detail returned in the GET for a single user than in the search results. Does it make it not RESTful if the same controller does return different data for a single object GET vs a GET returning a collection?
This is totally fine to return more details for a single object than for a collection. It doesn't affect RESTfulness of your API in any way.
COMMENT
You wrote:
PUT for update when specifying an ID
Actually it is not entirely correct:
PUT should be used for complete replacement of the entire entity
PATCH should be used to perform a partial update.

If you want to get passed through criteria as query string parameters to URI, you can do that using Attribute Routing into WebApi. I think attribute-routing-in-web-api helps to you.

Yes, I would pass filter parameters as query string options. The 'restfulness' of your application is not dependent on controller structure, so you can follow a structure that best suits your application

Related

Asp.Net Web Api : Handling two get request with same signature

Current situation
[HttpGet]
public HttpResponseMessage CheckProfileStatus(int id)
{
//check profile status from third party
return //profile status and HttpStatus;
}
[HttpGet]
public HttpResponseMessage ProcessTheProfile(int profileId)
{
//check profile status from third party again, if status is "Good", do something
return //someMessage and HttpStatus;
}
Url to call CheckProfileStatus method
myUrl/api/Profile/123
Url to call ProcessTheProfile method
myUrl/api/Profile/?profileId=123
I hope the comments in the code makes the situation clear.
I don't really like the fact that I have two HttpGet methods with same signature in one controller (even though it works).
I don't know if it is best practice.
Question:
Should I extract these two methods in two seperate controllers, is it completely fine to have two HttpGet methods with same signature in one controller or is there better way to handle this situation?
First, this is confusing to any client of the API.
You have 2 GET methods which are virtually identical. In fact most people prefer to add a route covering your first option which basically sets the profileId to the value after the forward slash.
Second, the expectation is that when you issue a GET against an entity with an ID, you get the representation of that entity. This is not what's happening here.
You need to decide which kind of API do you want. A generic one where stuff like this is fine :
myUrl/api/profile/process/123 or process/profile, or whatever else makes sense to the API.
If your API is supposed to be RESTful ( which basically means you will have one method per HTTP verb and nothing more ) then you need to add a new controller for ProcessProfile and then your url can look like this :
myUrl/api/ProcessProfile/123
None of these options is set in stone, they are just that, options.
The comments to the OP thread give some good options as well, which should be considered.

c# .net core 2 web api: correct HTTP result for missing parent object and / or validation?

Consider the following URL
This is a sample example, I'm using customers and order here, for an easy understanding of the setup.
In the order controller:
[HttpGet("~/api/customer/{customerId:int}/orders", Name = nameof(GetOrdersByCustomerIdAsync))]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<IEnumerable<OrderListItemResourceModel>>> GetOrdersByCustomerIdAsync(int customerId:int){}
Question 1: Should this method actually exists in the CustomerController or the OrderController?
For me it made more sense in the Order controller as I do return orders here, but the routings is that of a customer, ...
Question 2: In my Core & Data layer, the Order object has a customer linked to it. The binding model in my api layer has an integer CustomerId
Where do I validate that the given customer id actually is from an existing customer?
Is it ok to validate this in the controller by calling the customer service?
If so, can I just return NotFound()? Or should I do something else, so that it is clear to the user that it was the customer that is not found?
Again, this is a fictional example.
Question 1 - ~/api/customer/{customerId:int}/orders in Customers controller or Orders controller?
Customers Controller
Here you are accessing orders as a property of a customer, therefore that action falls under customer controller. But if you were accessing, say ~api/orders this would fall under Orders controller as this is accessing orders resources independent of any other resource.
Question 2 - where to validate customer Id against existing db records
This is rather dependent on your implementation. Asp.Net Core provides functionality to do this from virtually anywhere - Filters, Services, etc - via dependency injection. So this depends on the structure of your application.
Whether you can return NotFound() ?
Yes.
If customerId does not match any existing customer, NotFound would be the appropriate response according to the HttpStatusCode 404 Description.
In regards to the title/second question
For an API, not something that will be accessed via a web browser, you should return something more useful than 404 Not Found. I strongly disagree with using 404 for an API call that was successful, but had no data.
NoContent() (status code 204) would be better suited as there was no fault with the route or the call itself, there's just no content for it to give back.
As per the RFC 2616 HTTP protocol definition, 400 responses are for
Client Error - The request contains bad syntax or cannot
be fulfilled
There's no client error here, there's just no data for that ID. Whereas 200 responses...
Success - The action was successfully received,
understood, and accepted
This fits your scenario much better than the 400 responses, because the server received, understood and accepted the request.

Match controller method by GET parameter

I have a controller marked with [Route("api/entities")]. There is a method for getting all entities:
[Audit(ActionType.EntityList)] // custom annotation for audit
[Authorize]
[HttpGet]
public IActionResult GetEntities()
{
// ...
}
As you can see, by using annotations I save the request to some audit and authorize the request only to allowed users.
Now, I want to enhance this endpoint so it can return the top N entities. Example request: /api/entities?top=5. I have found that I should use an optional parameter for the method and use if to detect the case.
However, I need to save such call in audit as differnt type (e.g. [Audit(ActionType.EntityTop)]) and I do not need an authorization there (everyone can fetch the top entities).
How can I map the /api/entities request to one method and /api/entities?top=N to another? In Spring from Java I would use a params field of #RequestMapping.
I do not want to change the URL of this endpoint, because the top parameter modifies only the list that is being returned so one should use GET parameters for that. By having the same URL I also do not change the semantic meaning of the response (it is still list of the same entities). It is important when using RESTful frontend framework like Restangular.
Are you sure you need same action? It looks like you want to solve different tasks here, so I'd suggest you to create another action
[Audit(ActionType.EntityTop)]
[HttpGet("top/{top:int}")] //you can specify route via Annotations
public IActionResult GetEntities(int top)
{
// ...
}
Request with top entities will match /api/entities/top/5

Understanding [HttpPost], [HttpGet] and Complex Actionmethod parameters in MVC

I am very very new to MVC the design-pattern and also the Framework. I am also not extremely well- versed in fundamentals of ASP.NET Forms. However, I do understand the basics of web development and HTTP Post and GET as well.
Now, I have been going through some MVC tutorials and I though I was getting a good hold of how MVC the pattern works and also how "Routing Engine" works. Then suddenly I came across a code which looks like folloing:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content("Thanks", "text/html");
}
}
I have few questions looking at it:
My understanding of routing engine was that the control is passed to a particular ActionMethod based on URL and normally URL are basically Controller/ActionMethod/Id kind where paramter to action menthod are rather Primitive types. In this example above what kind of URL would it take to call "
public ActionResult Index(MyViewModel model)?"
Since NyViewModel is a complex type, you can not pass it as part of URL. How can you call it?
Why is this second method adorned with [HttpPost] when the first method does not require any attributes? Are there any guidelines on when to use [Http] attributes and when not?
I think I am missing a big pice in the puzzle and both the questions are interrelated. However, need some help in making sense with the relationship
The [HttpPost] attribute tells the routing engine to send any POST requests to that action method to the one method over the other. This is a type of overloading.
Why is this second method adorned with [HttpPost] when the first method does not require any attributes?
The default for a method is [HttpGet]. Because of that, no attribute is needed.
Are there any guidelines on when to use [Http] attributes and when not?
Ideally, attributes should be on every method, in order to avoid confusion. As you get more familiar with how things are working, you will often take shortcuts (as with everything else), and omit them when you know that they are not necessary.
Since MyViewModel is a complex type, you can not pass it as part of URL. How can you call it?
The data will be turned into the model from the data in the body of the request. This can come either as a JSON object, or as Form data. (There are tricks to get the object initialized from the URL, but they can be a little complicated and advanced.)
Generally, complex objects are passed in the HTTP body with verbs that support it such as POST and PUT. The body content must pass Model Binding validation. That basically means that if it's a POST request with Content-Type: application/json, it must deserialize from JSON into MyViewModel. If the content is XML, it must deserialize as XML.
General convention is to have all the primitive types that can be found in the URL path, query, and headers first, then one complex type from the POST (or PUT) body after that. I believe it's possible to put complex types elsewhere, but then you're getting into type converters and custom attributes which you should probably hold off on if you're a beginner.
Why is this second method adorned with [HttpPost] when the first method does not require any attributes? Are there any guidelines on when to use [Http] attributes and when not?
"[HttpPost]" is telling the routing engine that this method overload is only available via HTTP POST. Attempting to PUT /home/index with a body will fail with 404 Not Found, in this case. The parameter-free version of Index() doesn't require it because it can work with any HTTP verb, including GET, POST, and PUT.
Best Practice - Request handling
It is best practice to only use public methods in your controller which are going to be serviced either with a view or with json. For all public methods in your controller, it is best practice to either mark them with an [HttpGet] or an [HttpPost], or one of the other types which I wont cover as they are more edge case scenario.
These Http attributes restrict the method to only servicing those specific types of requests. While the default is [HttpGet], I have found that not marking [HttpGet] in all scenarios can at times lead to unexpected behavior when there are naming conflicts.
Best Practice - PRG
Post-Redirect-Get is a design pattern which essentially stipulates that any time you are going to be sending a response which came from a POST request, you should redirect to a get in order to send the response. This protects from a number of scenarios, including not posting again if the back button is used.
The redirect usually comes in the form of the [HttpPost] ActionResult using return RedirectToAction("MyHttpGetAction");.
Posting complex models
There are multiple ways which you can send a complex model. The main difference is that if you are using a GET request it is in the url, and if you are using a POST request it is in the request headers. If you use ajax then the difference becomes blurred as you will almost always send it in the body.

Designing Web API controller

In existing LOB app, I added new Web API project. Because of existing layers in app (Domain objects,DAL-ADO.NET-DataReader, BLL), I decided to use the Web API by holding the existing logic.
First question: Is this a right way?
There is a method in BLL which return list of object, and receives 4 parameters. All of this input parameters can be NULL, and in that case,the method returns full list of objects.
Second question: How to design WEB api controller for the aforementined method?
public static List<DomainObject> GetTata(int? param1,int? param2, int? param3, int? param4)
{
List<DomainObject> return = new List<DomainObject>();
using (Context context = new Context())
{
return = MyDAL.GetData(param1,param2,param3,param4, context);
}
return return ;
}
Don't know what Data means, but when designing a WebApi you should think in "Resources" (instead of thinking in "Actions").
I don't like the method to be static (honestly, never tested it and i don't know if works), but methods that serve responses should be instance methods (and sooner or later you will probably need to access some instance field).
Don't know what the four parameters are, but you should think about the URL for accessing this controller. Lets assume that this controller returns something like "Customers". So the URLs i think are like:
/api/Customers -> Get ALL customers
/api/Customers/{id} -> Get Customer of this id
Additional filters and clausules (order by, pagination if needed) are usually passed through querystring (i.e. take a look on how OData do this). Something like:
/api/Customers?name=foo -> Get ALL customers which its name starts with foo
/api/Customers?name=x&order=birthDate -> Get ALL customers which its name starts with x and ordered by birhDate.
So, your controller should translate between the URL parameters (route and querystring) to the parameters expected by the DAL classes. But exposing the same parameters that DAL classes expect to the WebApi most of times is not a good idea.
Hope this helps... For more info you should give some more info (what the parameters means, and so on). ;)

Categories

Resources