API method which does not Post anything and does not Get anything - c#

I have next API methods:
Restore database (base on file name).
Copy something from folder A to folder B
So this are API method which does not Post anything and does not return(Get) anything.
Should I use Post,Put or Get in case like this?
Currently I am using Get, is this OK?
It make sense to use Get when we return some data, but I don't need to return any data.
At the same time I don't need to post any object, I only need identifier.
Restore from folder on server base on 'id' and 'file name':
[HttpGet]//**put?
[Route("api/[controller]/[action]/{restoreFileName}")]
public async Task<IActionResult> RestoreDB(string restoreFileName)
{
//Restore database from folder A on server base on restoreFileName
return new OkResult();
}
Copy from folder A to folder B:
[HttpGet]//**put?
[Route("api/[controller]/{id}/[action]/{filename}")]
public async Task<IActionResult> CopyFromAtoB(int id, string fileName)
{
//Copy from folder A to folder B base on 'id' and 'file name'.
return new OkResult();
}

The problem is your API doesn't follow REST principles. Endpoints should represent resources on which you can perform different actions (GET, POST, PUT, PATCH, DELETE). Your endpoint:
[Route("api/[controller]/[action]/{restoreFileName}")]
Doesn't represent any resource. It's just an URI path. Neither controller, action or restoreFileName is a resource.
So, you have two options.
Just ignore any REST conventions and just create your own custom paths. If you choose this option, you don't need to do anything. You can use any verb you wish, any naming you wish. However, even in such case POST verb is the most natural one (it does make some change to the system).
Refactor your API to follow REST.
If you choose the second option, then your routes should like like this:
[HttpPost]
[Route("api/fileManager/files/{filename}")]
And use a command which you will send in the POST body to distinguish between operations on the file, so e.g.: {action: "restore"}

Related

Insert a routing parameter before {controller}

I have a working API with a bunch of controllers, with a single database specified in config file.
Now I want to make the the API multi database and make the target database a part of the url.
I use attributes on controllers now and default routing.
Startup.cs:
app.UseMVC();
FolderController.cs:
[Route("api/[controller]")]
[ApiController]
public class FoldersController : ControllerBase { ...
and action on controller:
[HttpGet("{Parent:Guid}", Name = "Get")]
public IActionResult Get(Guid Parent) {...
So what that gives me is the standard overall template that looks like this:
https://api.example.com/api/{controller}/{action}
What I'd want is to make the database a part of the url, the intuitive place being in front of the controller. I can also skip the second api bit as I'm not running anything else on that base address.
https://api.example.com/{database}/{controller}/{action}
I've been able to extract the database name by changing the controller attribute to:
[Route("{database}/[controller]")]
But then I'd have to insert code in every action method to check for route etc, with the risk of not implementing it consitently (beside the extra typing).
Ideally I'd like to add this to the default route in startup.cs, and add a service to the middleware that would check the privileges for the authenticated user on the requested database and continue as appropriate. That way I'd have my security in one place and no way to forget it in a controller.
I havent been able to figure out how to mix that with the attributes, they seem to conflict with each other.
Can this be done? Does anyone have some pointers for me get out of this?
By understand I know we can do it. You need to implement IHttpHandler.
You can refer to the following example https://www.c-sharpcorner.com/article/dynamic-and-friendly-url-using-mvc/

ASP.NET Post Endpoint: Data model in body vs query parameters

Making an API responsible for storing files.
The File data model looks something like this
public class File
{
public Guid Id { get; set; }
public string Path { get; set; }
public string Contents { get; set; }
}
So with the post request I setup in my controller (to create the file), I need the id of the file, the path the file will be stored (relative to a root path I have pre-configured), and the contents of the file.
I'm wondering if it's better practice to require a File data model to be passed in the body of the request, or to have the id and path both as query parameters and the contents in the body of the request.
TL;DR - use PUT /files/{id} with the content in the body.
What this depends on is - what is the (unique to your system) identity of a File? I'll assume here that it's the Id passed in by the client, and that Path is simply metadata. If path forms part of the identity as well, you'll need to adjust this accordingly.
As it's the client that supplies and determines the identity, you should probably prefer a PUT to a POST, and supply the identity in the path; e.g. PUT /files/{id} with the path and content in the body. A subsequent identical PUT leaves the system in the identical state, so it satisfies the requirement that a PUT is idempotent to observers. A subsequent non-identical PUT updates the existing content.
The HTTP spec allows some flexibility here, so you can use a POST. The latest has changed the definition of POST a little (to have broader application), but in the original (emphasis mine)...
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.
... it was clearer that POST /files makes sense to create e.g. a file in a directory (their example), but POST /files/{id} makes less sense.
This discussion has some more on PUT vs POST. It's also asserted there that:
Note that the following is an error:
POST /questions/<new_question> HTTP/1.1
Host: www.example.com/
If the
URL is not yet created, you should not be using POST to create it
while specifying the name. This should result in a 'resource not
found' error because <new_question> does not exist yet. You should PUT
the <new_question> resource on the server first.

OData v4 Custom Action for File Upload

I have an OData controller with standard verbs for CRUD. Everything is working fine. Now I need to add a custom action to perform file upload. I try to add a method to my existing controller like this:
[HttpPost]
[Route("UploadFile")]
public async Task<HttpResponseMessage> UploadFile()
{
//handle uploaded content logic here...
}
But when I try to invoke it by doing a POST:
http://localhost/UploadFile
I get this error:
System.InvalidOperationException: No non-OData HTTP route registered.
What should I do for this custom action to allow file upload?
You need to declare the Action as part of the EdmModel, in the following example I am assuming that your Entity Type is Attachment, and your controller class name is AttachmentsController. By convention, your EntitySet name must then be Attachments
var attachments = builder.EntitySet<Attachment>("Attachments");
attachments.Action(nameof(AttachmentsController.UploadFile))
.Returns<System.Net.Http.HttpResponseMessage>();
The important part of the above statement is the return type, if you do not declare the return type correctly in your EdmModel then you will find your endpoints returning 406 errors - Unacceptable, even though your method executes correctly, which is really confusing the first time you run into it. This is because OData will still try to parse your response to match the Accept header from the request before completing the response.
Try to use 'nameof' when mapping functions and actions instead of 'magic strings' or constants so that the compiler can pickup basic issues like wrongly defined route.
With this approach you do not need the Route attribute on the method header and the action will be included in the metadata document and therefore swagger outputs.

c# webapi - The requested resource does not support http method 'GET'

I have two simple routing methods:
main routing directive:
[RoutePrefix("api/accounts")]
first one
[Route("user/{id:guid}", Name = "GetUserById")]
public async Task<IHttpActionResult> GetUser(string Id)
second one
[Route("user/del/{id:guid}")]
public async Task<IHttpActionResult> DeleteUser(string id)
I wonder why if I test the first method with direct ulr ( GET ) it works:
http://localhost/ReportApp_WebApi/api/accounts/user/1533882b-e1fd-4b52-aeed-4799edbbcda6
And if I try the second link that is just a little different:
http://localhost/ReportApp_WebApi/api/accounts/user/del/1533882b-e1fd-4b52-aeed-4799edbbcda6
I get: The requested resource does not support http method 'GET'.
Can you help me?
The second link is not just a little different. In fact it is pointing to the route that you have defined for your delete method. This method expects DELETE Http verb.
When you write the url directly on the browser it performs a GET request. If you want your delete method to work with a GET verb you have to put the attribte
[HttpGet]
before or just after your Route atttribute. Although this I would not recommend to do it. You can have a HTTP client like Fiddler or Postman to test this
Web Api uses conventions when naming methods in your controllers. Because you named your method DeleteUser, Web Api expects Delete verb.
EDIT>>>Please follow recommendations listed in comments. I have also make bold the part where I do not recommend this

"True" REST routing via MVC 4 Web API

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.

Categories

Resources