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

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.

Related

C# HttpRequestMessage.GetCorrelationId method returns duplicates

My team and I have been working on this issue for a couple of days and we can't determine the root cause why GetCorrelationId() returns duplicates GUID sometimes.
Within the application that I'm working right now we use the correlation id to tie up the request path.
For example the UI sends a Save request to the API in .NET. When the API calls the save method in the query service from the controller, it pass through the result of Request.GetCorrelationId() to the method call.
The save method in the query service uses this parameter to insert a new row in the audit_logs table with the request information.
The save method then calls other save methods on nested objects that belongs to the main Object, passing the correlation_id generated in the controller.
Something like this
[ Controller ]
var correlation_id = HttpRequestMessage.GetCorrelationId();
{
ParentObject.save(correlation_id) -> {
ChildObject1.save(correlation_id),
ChildObject2.save(correlation_id),
}
}
My question is. Is this an issue of how GetCorrelationId creates the GUID object or there is something wrong with the configuration of the framework?
The framework is .NET Framework 4.6.1
We see this issue in IIS Express and IIS server
I can't share code but I'll do my best to share as much information is needed to troubleshoot this issue.
This are some examples of duplicates GUID
Thanks
OK I found a solution. I'm posting it so if someone else has the same issue can get this example.
After some more digging on Google I found this post
https://www.codeproject.com/Articles/392926/Grouping-application-traces-using-ActivityId
Sebastian is using the ActivityId generated by IIS to tied together the different commands executed during his process.
Then I found the source code of HttpRequestMessageExtensions https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Http/HttpRequestMessageExtensions.cs
There, you can see that the method GetCorrelationId() uses the ActivityId to assign a correlation ID to the request.
ActivityId is not completely random. It is tied to the thread started by IIS. I notice that all our request from localhost:port have the same string at the end b63f-84710c7967bb.
We wanted to tied all the commands executed to save an object or retrieve it. So all we needed was a random Guid generated per request. Following the example posted by Sebastian, I stored the current ActivityId in an aux variable, then assigned a new GUID to it, executed Request.GetCorrelationId() and finally assigned back the ID stored in the aux variable to the ActivityId.
In pseudo code =>
Guid prevActivityId = Trace.CorrelationManager.ActivityId
Trace.CorrelationManager.ActivityId = Guid.NewGuid()
...
guid correlation_id = Request.GetCorrelationId()
...
Trace.CorrelationManager.ActivityId = prevActivityId
Hope this is helpful to someone else.

API method which does not Post anything and does not Get anything

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"}

Why does ASP.NET Web Api model binding uses the parameter type to determine the source of the value?

Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.

"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.

MVC Routes - How to get a URL?

In my current project we have a notification system. When an oject is added to another objects collection, an email is sent to those who are subscibed to the parent object. This happens on the object layer and not in the View or Controller.
Here's the problem:
Although we can say who created what with what information in the email, we cannot embed links to those objects in the email because in the object layer there is no access to a UrlHelper. To construct a UrlHelper you need a RequestContext, which again does not exist on the object layer.
Question:
I want to make a helper class to create the url's for me. How can I create an object that will generate these urls without a request context? Is it possible?
The problem is compounded by the fact that you don't want a relative URL in an email, you want an absolute email so you need to hard-code the domain too because there is no request to grab it from.
Another factor is that emails can outlive the current site structure by months or years so you need a kind of permalink, and thus a way to associate multiple Urls with a single action (additional routes). This latter issue is also a factor in SEO where you don't want to leave any page behind.
For now a static method on your controller UrlToActionX(params) sitting next to the method ActionX seems like the simplest workaround. All it does is the appropriate string.Format(...) on the id's of the strongly-typed parameters to generate the permanent Url. Add a static domain on the front, or a domain from the user object (since you know which domain they visit when they come to your site) and you have your email link.
It's not ideal but at least you now have only one place to maintain the Url generation.
IMHO: When it comes to permanent links to a changing web site sometimes it's better to rely on "configuration over convention". :-)
I'm not aware of a way to do this, you MUST have access to the routes at the very least to make your own helper. Unless your business objects know about the registered routes, you can't get away from doing some hard-coding.
Here is how you might limit the hard-coding of urls though...
Code in a url with all the relevant bits in your object's methods..
class Event
{
public void SendEmail()
{
var url = string.Format("http://myurl.com/r/Event?eventId={0}", EventId);
//send emails...
}
}
Note the /r/Event piece of the url. This would be a map to a RController that would be responsible for taking arbitrary, made-up links and sending a 301 Permanent Redirect and going through the route engine to create a real url using the current routes. This way you are only hard-coding a utility controller url and not to the ever evolving controller actions of your real pages.
class RController : Controller
{
public ActionResult Event(int eventId)
{
Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
Response.RedirectLocation = Url.Action("Details", "Event", new { eventId = eventId });
return null;
}
public ActionResult Register(int eventId)
{
Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
Response.RedirectLocation = Url.Action("Register", "Event", new { eventId = eventId });
return null;
}
}
It just feels a bit better than hard-coding a bunch of different controllers/actions that you might decide to rename later. Think of it as your own little TinyUrl like service.
You could define an interface with a method that takes whatever information is necessary to create a URL (object ids or whatever) and returns a URL. Write an implementation of that interface that uses the UrlHelper to do this work, and then supply this to your object layer (ideally with an IoC container).
You could use:
VirtualPathUtility.ToAbsolute(string.Format("~/r/Event?eventId={0}", id))
to resolve the url. Still not nice though.

Categories

Resources