Correct HTTP verb to use for a business rules validation endpoint in Web API - rest

I have the below Web API controller. Its sole responsibility is to validate the incoming document against a set of business rules and return a result. Which is the correct HTTP verb to use for this controller action?
//[Http<???>]
public IActionResult ValidateBusinessRules([FromBody BusinessDocument document)
{
var result = ValidateBusinessRules(document);
return Ok(result);
}

[FromBody] explicitly tells the model binder to check the request body for data to bind. And since only certain request allow a body then it means that it works with POST or PUT.
POST would be the default verb to use in this scenario. Taking model state into consideration the action can look like this
[HttpPost]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document) {
if(ModelState.IsValid) {
var result = ValidateBusinessRules(document);
if(result.IsValid) { //assuming result has a flag
return Ok(result);
}
return BadRequest(result);//assuming result provides relevant details.
}
return BadRequest(ModelState);
}
That way the status of the response can provide some relevant feedback about the request made.

One could argument that POST should only be used for creating a new entity but as GET is not designed to send data through the request body and the other verbs (PUT = update entity, DELETE = remove entity) don't give you a better option I would say it's OK to use POST for scenarios where you need to get some data from the server and need to send data in the request body.
I would therefore recommend you to use POST here
[HttpPost]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document)
{
var result = ValidateBusinessRules(document);
return Ok(result);
}

If you use this endpoint to validate data from form, and then you want to save them through another endpoint, I think, that the best solution would be something like this:
[HttpPost]
[Route("documents")]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document)
{
var result = ValidateBusinessRules(document);
if (!result.IsValid)
{
return BadRequest(result);
}
var document = _documentService.Save(document);
return Ok(document);
}
For me it is odd to use POST if you don't want to create new resource.

Related

How to design multiple ways to invoke REST API

I am using ASP.NET Web API. I want to REST uri to be
GET /api/v1/documents/1234/download or
GET /api/v1/documents/1234?act=download or
GET /api/v1/documents?id=1234&act=download
Is it possible to have multiple ways to call REST API Url? Is it recommended?
I am using Attribute Routes only
[RoutePrefix("api/v1")]
public class DocumentController : ApiController
{
private readonly DomainService _domainService;
public DocumentController(DomainService domainService)
: base(domainService)
{
_domainService = domainService ?? throw new ArgumentNullException(nameof(domainService));
}
[HttpGet]
[Route("documents/{id:int}")]
public async Task<IHttpActionResult> DownloadDocument([FromUri]int id, [FromUri]string act)
{
if (string.IsNullOrEmpty(act) || act.ToUpper() != "DOWNLOAD")
{
return BadRequest("Invalid action parameter.");
}
return await service.DownloadFile(id);
}
}
with above code only GET /api/v1/documents/1234?act=download works. Is it possible to configure route in a such way that all 3 routes will invoke same action method?
You can add as many Route attributes as required to each method.
So you could do this to your method:
[Route("documents")] // matches /documents?id=123&act=download
[Route("documents/{id:int}")] // matches /documents/123?act=download
[Route("documents/{id:int}/{act}")] // matches /documents/123/download
Personally I think this is quite long-winded, and would try to stick to a single style (the last one if I could choose), but I guess it could depend on your requirements.

Spring Boot REST API Endpoint Mapping best practice

I am using below endPoint URL Mapping with HTTP Methods like ( POST, DELETE, GET, PUT)
POST for Create a new Trade -
#PostMapping("/trade")
DELETE for Delete a Trade with specific id -
#DeleteMapping("/trade/{id}")
GET for Get details of specific Trade -
#GetMapping("/trade/{id}")
PUT for Update Trade details -
#PutMapping(“/trade/{id}”)
GET for Retrieve all Trade list of the collection -
#GetMapping("/trades")
Spring currently supports five types of inbuilt annotations for handling different types of incoming HTTP request methods which are GET, POST, PUT, DELETE and PATCH. These annotations are:
#GetMapping
#PostMapping
#PutMapping
#DeleteMapping
#PatchMapping
From the naming convention, we can see that each annotation is meant to handle the respective incoming request method types, i.e. #GetMapping is used to handle GET type of request method, #PostMapping is used to handle POST type of request method, etc.
if I am missing anything here Please suggest
Add API version like
#RestController
#RequestMapping("/API/V1")
public class TestController {
#RequestMapping("/greeting")
public String greeting( {
return "welcome";
}
}
For versioning there are several approaches you can use:
URI path:
Include the version number in the URL path of the endpoint.
For example v1 in /api/v1/trade:
public class TradeController {
#GetMapping("v1/trade")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping("v2/trade")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Query parameters:
Pass the version number as a query parameter with a specified name.
For example: ?version=1 in /api/trade?version=1:
public class TradeController {
#GetMapping(name = "v1/trade", params = "version=1")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping(name = "v2/trade", params = "version=2")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Custom HTTP headers:
Define a new header that contains the version number in the request.
Content negotiation:
Include the version number in the Accept header along with the accepted content type.

Wep Api - 405 Method Not Allowed

I have a Web Api project with a controller that has methods for GET, DELETE, POST, and PUT.
When I try to do a POST or PUT to this controller I always get a 405 Method Not Allowed error. The data being sent over looks valid, it's just an object with six simple properties. I put a breakpoint in my method and as expected in this case, it doesn't get hit. I registered a DelegatingHandler (mentioned at Web Api - Catch 405 Method Not Allowed) to inspect the incoming request and outgoing response and I can tell that my request is being processed by the Api (meaning the problem is not with the client). I also used Fiddler to inspect the request/response and the response headers say under Security, Allow: DELETE, GET.
This clearly tells me that PUT and POST are not allowed, for whatever reason, even though I have methods decorated with the [HttpPost] and [HttpPut] attributes and have the routing configured correctly, as far as I can tell. I am using default routing but also have methods which use attribute routing.
This sounds like there may be some kind of security issue, however, I'm able to do POST and PUT in my other controllers and I don't see any differences which I believe would be the cause of the problem.
Here's a snippet of my code:
public class PricesController : ApiController
{
// DELETE: api/Prices/5
[HttpDelete]
[ResponseType(typeof(Price))]
[Route("api/Prices/{id:int}")]
public async Task<IHttpActionResult> DeletePrice(int id)
{
// code omitted
}
// GET: api/Prices/5
[HttpGet]
[ResponseType(typeof(Price))]
[Route("api/Prices/{id:int}")]
public async Task<IHttpActionResult> GetPrice(int id)
{
// code omitted
}
// GET: api/Prices
[HttpGet]
[Route("api/Prices")]
public IQueryable<Price> GetPrices()
{
// code omitted
}
// POST: api/Prices
[HttpPost]
[ResponseType(typeof(Price))]
[Route("api/Prices", Name = "Prices")]
public async Task<IHttpActionResult> PostPrice(Price price)
{
// code omitted
}
// PUT: api/Prices/5
[HttpPut]
[ResponseType(typeof(void))]
[Route("api/Prices/{id:int}")]
public async Task<IHttpActionResult> PutPrice(int id, Price price)
{
// code omitted
}
}
Any help would be appreciated. I've spent all day trying to figure this out.
It sounds like it's not binding correctly.
Can you try decorating Price with [FromBody] before it in your actions?
PostPrice([FromBody] Price price)

How to return related entities when inserting/posting a new one using OData Web Api

I've created an OData v4 endpoint using ASP.NET Web API 2.2 and wondering how to return related entities when inserting/posting a new one?
Controller:
public async Task<IHttpActionResult> Post(LibraryFolder libraryFolder)
{
this.db.LibraryFolders.Add(libraryFolder);
//Add MediaFiles to the conext
this.businessController.RefreshLibrary(libraryFolder);
await this.db.SaveChangesAsync();
//The libraryFolder object contains the MediaFiles at this point, but they get stripped out when going back to the client
return this.Created(libraryFolder);
}
Simply to add [EnableQuery] on the Post action. For example:
[EnableQuery]
public async Task<IHttpActionResult> Post(LibraryFolder libraryFolder)
{
...
}
Now, send the Post request to the Uri with $expand clause. For example:
POST http://..../odata/LibraryFolders?$expand=XXXX
Content-Type: application/json
Content: {...}

ASP.NET Web API REST Querystring - How does a client know available parameters and options?

When exposing querystring parameters using GET I have the following base URL:
https://school.service.com/api/students
This will return the first 25 students.
What if I want to return a list of students based on ONE of the following criteria:
* have accepted a job
* have received a job offer
* have no job offers
The three above choices are essentially an enum.
Therefore, the query request for students who have no job offers I assume would look like:
https://school.service.com/api/students?jobOfferStatus=3
However, I'm wondering if jobOfferStatus=3 is the proper way to handle this. If so, how would I publish/provide to the clients a list of available options for that jobOfferStatus query parameter? What about other possible query parameters and their valid options? We'll have many possible query parameters like this.
I'd love to see an example of how this should be done properly. What are the best practices?
There are two main options: documenting it, or making it discoverable. A lot of APIs have documentation where they list all of the resources and parameters for reference. Otherwise, the client won't know.
You could also make it discoverable in some way by including the options in a response. For conventions on this, search for HATEOAS if you haven't already. (I'm not really knowledgeable enough about HATEOAS myself to make a suggestion.)
I will mention that "3" is not a very meaningful value for jobOfferStatus, and there's no need for the client to know that number. You can make it anything you want -- jobOfferStatus=none or even jobOffer=none. Your controller can do the work of matching that value to your enumeration. Try to design your interface to be intuitive for developers (and, of course, write good documentation).
To handle multiple query parameters, you can use optional parameters in your function:
public HttpResponseMessage GetStudents(string jobOffer = "",
string other1 = "",
string other2 = "")
{
if (jobOffer == "accepted" && other2 == "whatever") {
// return a response
}
else {
// return a different response
}
}
When the client uses parameters by those names, you can tailor your response appropriately.
You have some options to do this, let's try to help:
1) Configure a generic route to asp.net web api knows how to solve another action's name different from Get to a get method, on the App_Start\WebConfigApi.cs class, try to add this:
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
Using it, you can have diferent methods on the api controller:
// request: get
// url: api/Students/GetStudents
public HttpResponseMessage GetStudents()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsWithJobOffer
public HttpResponseMessage GetStudentsWithJobOffer()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsAcceptedJob
public HttpResponseMessage GetStudentsAcceptedJob()
{
return Request.CreateResponse(...);
}
2) Use a simple parameter on the Get method:
// request: get
// url: api/Students?jobOfferStatus=1
public HttpResponseMessage GetStudents(int jobOfferStatus)
{
// use jobOfferStatus parameter to fill some list
return Request.CreateResponse(...);
}
3) Use a simple method with a parameter named id, to get a default friendly url by asp.net mvc web api.
// request: get
// url: api/Students/1
public HttpResponseMessage GetStudents(int id)
{
// use the id parameter to fill some list
return Request.CreateResponse(...);
}