In all the ASP.NET WebAPI examples I have seen for implementing the HTTP PUT method, the record's key is passed as a separate parameter to the model updates. For example:
[HttpPut]
public HttpResponseMessage Put(int id, UserEditViewModel model)
{
// Look up existing record
User user = await db.Users.FindAsync(id);
// Apply changes
// user.Name = model.Name;
// Commit updated record to data store
db.SaveChanges();
}
I am curious as to why this approach is used rather than to define the key value on the model and simplify the call?
public HttpResponseMessage Put(UserEditViewModel model)
{
// Look up existing record
User user = await db.Users.FindAsync(model.UserId);
// Apply changes
// user.Name = model.Name;
// Commit updated record to data store
db.SaveChanges();
}
In most cases that I can think of the View will require the UserId anyway, so I don't see how/why it would complicated the model from the View's perspective, but I am sure there must be a good reason.
Depending on what level of RESTfulness we're talking about, I think it might be more about convenience. Looking at Richardson's Maturity Model level 3, the workflow could be something like this:
GET /api/users/{id}
Once the client navigates to a user, the server will have built the link uri, filling it with the unique resource identifier, so it could look something like this:
"api:user-edit": {
"href": "http://apiname:port/api/user/{id}"
},
So the client will have to just do a
PUT /api/users/{id}
with the appropriate payload (which theoretically should be the resource in full, but more often than not the server will only choose to look at just a couple of fields - almost like a PATCH).
Related
In OData V3, I can select just fields from parent/ancestor entities like this:
http://services.odata.org/V3/Northwind/Northwind.svc/Order_Details(OrderID=10248,ProductID=11)?&$select=Product/Category/CategoryName&$expand=Product/Category
That query returns only CategoryName, it does not include any fields from Order_Details or Product. This behavior is very important to our application for performance reasons. Selecting all fields when we don't need them can have a significant impact on query performance.
There does not seem to be a way to accomplish the same in OData V4. The equivalent query returns all fields from Order_Details and Product
http://services.odata.org/V4/Northwind/Northwind.svc/Order_Details(OrderID=10248,ProductID=11)?$expand=Product($expand=Category($select=CategoryName))
The closest I can get is to just select one field from each level, introduces a lot of complexity into our code, and it has been difficult to ensure that all queries (future and existing) adhere to this rule.
The closest I can get is to just select one field from each level,
introduces a lot of complexity into our code, and it has been
difficult to ensure that all queries (future and existing) adhere to
this rule.
Looks something like this:
http://services.odata.org/V4/Northwind/Northwind.svc/Order_Details(OrderID=10248,ProductID=11)?$expand=Product($select=Category;$expand=Category($select=CategoryName))&$select=Product
There is certainly a bit of added complexity here, but this was acceptable in my case.
The syntax for this is:
https://services.odata.org/V4/Northwind/Northwind.svc/Order_Details(OrderID=10248,ProductID=11)/Product/Category?$select=CategoryName
results in:
{
"#odata.context": "https://services.odata.org/V4/Northwind/Northwind.svc/$metadata#Categories(CategoryName)/$entity",
"CategoryName": "Dairy Products"
}
The key OData v4 concept here is that the path, that is everything before the ? defines the resource that is being served, and by that the shape of the resulting graph. The output of $select and $expand (projections) is constrained to match the requested resource.
So in v3 with $select you could return a more arbitrary structure but in v4 the $select and $expand can only mask the graph by returning what is essentially a subset of $select=*&$expand=*.
To get around this but still allow similar query scenarios in v4 we can compose an entity path expression to *any resource within the parent path.
So we move the resource selector path from the v3 $select Product/Cateogry' and append it the path of our resource ~/Order_Details(OrderID=10248,ProductID=11)`
NOTE: There is a strong caveat to this, whilst the OData specification describes this behaviour, not all implementations support deep resource selections like this. The specification is a guidance document on the standard protocol, not all implemenations are 100% compliant.
A simplification of this is to try selecting just the Product from the same query, notice here we do not use any query parameters at all:
~/Order_Details(OrderID=10248,ProductID=11)/Product
{
"#odata.context": "https://services.odata.org/V4/Northwind/Northwind.svc/$metadata#Products/$entity",
"ProductID": 11,
"ProductName": "Queso Cabrales",
"SupplierID": 5,
"CategoryID": 4,
"QuantityPerUnit": "1 kg pkg.",
"UnitPrice": 21.0000,
"UnitsInStock": 22,
"UnitsOnOrder": 30,
"ReorderLevel": 30,
"Discontinued": false
}
You can see in this response that the context or the resource that is being returned is a $metadata#Products/$entity and not an Order_Details/$entity
Once your resource is selected, normal v4 $select and $expand logic is evaluated. This is documented in the specification under 4.3 Addressing Entities
These rules are recursive, so it is possible to address a single entity via another single entity, a collection via a single entity and even a collection via a collection; examples include, but are not limited to:
By following a navigation from a single entity to another related entity (see rule: entityNavigationProperty)
Example 14:
http://host/service/Products(1)/Supplier
Update:
I've substantially edited this post from my original answer, at the time i misinterpreted OP's request and the structure they were expecting, but this is still relevant information in 2022 and none of the answers directly produces the desired behaviour.
The simplest solutions would be to create View with required schema on your db server and try to fetch data from this datasource with filters and column name(s) instead.
Especially when facing issues with performance.
The best way would be to register this class to your IoC as singleton
public class InternalODataEdmModelBuilder
{
private readonly ODataConventionModelBuilder _oDataConventionModelBuilder = new ODataConventionModelBuilder();
private IEdmModel _edmModel;
public InternalODataEdmModelBuilder()
{
ODataEntitySetsConfigInternal.Register(_oDataConventionModelBuilder);
}
// cache
public IEdmModel GetEdmModel()
{
return _edmModel ?? (_edmModel = _oDataConventionModelBuilder.GetEdmModel());
}
}
internal static class ODataEntitySetsConfigInternal
{
public static void Register(ODataConventionModelBuilder oDataModelBuilder)
{
if (oDataModelBuilder == null)
{
throw new Exception("'ODataConventionModelBuilderWebApi' cannot be null");
}
oDataModelBuilder.EntitySet<YourView>("YourView").EntityType.HasKey(x => x.YourKey);
}
}
And then inject this registered object in your API controller and build your query from URL like this:
ODataQueryContext queryContext = new ODataQueryContext(_oDataConventionModel, typeof(YourViewEFType), null);
var option = new ODataQueryOptions(queryContext, Request);
var data = option.ApplyTo(data, new ODataQuerySettings { EnsureStableOrdering = false });
And then map data into your POCO (API EDM model shown to the public world).
Hope this helps.
I'm creating a new Web API and I'm having a doubt regarding the Update operation (it's a basic CRUD). Should I return a DTO with the updated entity data? I want my API to be RESTful.
have a read here
https://www.rfc-editor.org/rfc/rfc7231
it says and I quote:
For a state-changing request like PUT (Section 4.3.4) or POST
(Section 4.3.3), it implies that the server's response contains the
new representation of that resource, thereby distinguishing it from
representations that might only report about the action (e.g., "It
worked!"). This allows authoring applications to update their
local copies without the need for a subsequent GET request.
However, you do not need to be too fixed on this, return a 201 for example when you create something is perfectly OK as well and you probably want to add the the unique identifier of the created resource.
For updates, a 200 would be ok as well. 204 can be acceptable as well as already mentioned.
The bottom line is ... return only the data you need, if you need to see the whole updated object then return it. If you don't then don't do it. Keep in mind that some objects can be quite big and have a whole object graph below them, there's no point sending too much data down the wire.
I guess the most important thing is to choose one way of doing things and then be consistent and use the same thing everywhere
First of all, returning a DTO has nothing to do with RESTful.
It's true that DTO is a pattern created with the purpose of transferring data to remote interfaces (and web services can be a good fit for this pattern).
However using DTOs won't make your application more or less RESTful. Your application can use DTOs to have more control over the data exposed in the REST API. Just that.
If your update operation relies on the PUT HTTP method (which is designed to replace the state of a resource with a new representation), you may want to return 200 or 204 status code to indicate that the operation has succeeded.
If you go for 200, you can return a representation of the new state of the recently updated resource. If you go for 204, no representation must be returned.
By representation I mean a JSON document, a XML document or any other content that can be used to represent the state of a given resource.
We normally return NoContentResult after update is successful. For example,
[HttpPut("{id}", Name = "UpdateUser")]
public IActionResult UpdateUser(Guid id, [FromBody] UserUpdateDto user)
{
if (user == null)
{
return BadRequest();
}
if (!_repository.UserExists(id))
{
return NotFound();
}
var entity = _repository.GetUser(id);
Mapper.Map(user, entity);
_repository.UpdateUser(entity);
return NoContent();
}
NoContent basically returns status code 204. The following is the source code of NoContentResult.
public class NoContentResult : StatusCodeResult
{
public NoContentResult()
: base(204)
{
}
}
Returning data from a PUT operation is optional, though not necessary. If theres anything you wanted to calculate in the model which will be useful for the client then return them, but otherwise a 204.
I am using the Asp.Net Web Api. I would like to be able to filter out certain fields on the response objects based on the connected clients access rights.
Example:
class Foo
{
[AccessFilter("Uberlord")]
string Wibble { get; set; }
string Wobble { get; set; }
}
When returning data the filed Wibble should only be returned if the current users context can satisfy the value of "Uberlord".
There are three avenues that I am exploring but I have not got a working solution:
A custom WebApi MediaTypeFormatter.
A custom json.net IContractResolver.
Some sort of AOP wrapper for controllers that manipulates the response object
My issue with these are:
The custom formatter does not feel like the right place to do it but might be the only option.
The custom json serializer would not have access to the current context so I would have to work that out.
With the first two options you would require specific implementations for each response format, json, xml, some custom format, etc. This would mean that if another response type is supported then a custom formatter / serializer is required to prevent sensitive data leaking.
The AOP controller wrapper would require a lot of reflection.
An additional bonus would be to strip out values from the fields on an inbound request object using the same mechanism.
Have I missed an obvious hook? Has this been solved by another way?
It was actually a lot simpler than I first thought. What I did not realise is that the DelegatingHandler can be used to manipulate the response as well as the request in the Web Api Pipeline.
Lifecycle of an ASP.NET Web API Message
Delegating Handler
Delegating handlers are an extensibility point in the message pipeline allowing you to massage the Request before passing it on to the rest of the pipeline. The response message on its way back has to pass through the Delegating Handler as well, so any response can also be monitored/filtered/updated at this extensibility point.
Delegating Handlers if required, can bypass the rest of the pipeline too and send back and Http Response themselves.
Example
Here is an example implementation of a DelegatingHandler that can either manipulate the response object or replace it altogether.
public class ResponseDataFilterHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
//Manipulate content here
var content = response.Content as ObjectContent;
if (content != null && content.Value != null)
{
((SomeObject)content.Value).SomeProperty = null;
}
//Or replace the content
response.Content = new ObjectContent(typeof(object), new object(), new JsonMediaTypeFormatter());
return response;
});
}
}
Microsoft article on how to implement a delegating handler and add it to the pipeline.HTTP Message Handlers in ASP.NET Web API
I have a similar question in the works over here: ASP.NET WebAPI Conditional Serialization based on User Role
A proposed solution that I came up with is to have my ApiController inherit from a BaseApiController which overrides the Initalize function to set the appropriate formatter based on the user's role. I haven't decided if I will go this way yet, but perhaps it will work for you.
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// If the user is in a sensitive-data access role
controllerContext.Configuration.Formatters.Add(/*My Formatter*/);
// Otherwise use the default ones added in global app_start that defaults to remove sensitive data
}
I've an entity with an ID of
public string ID {get;set;}
activities/1
(which comes from RavenDB).
I'm registering the following routes in my ServiceStack AppHost
Routes
.Add<Activity>("/activities")
.Add<Activity("/activities/{id}");
I'm using a backbone app to POST and PUT to my REST Service.
What happens out-of-the-box:
id property is serialized into the json as "activities/1"
id property is encoded into route as "activities%2F1"
ServiceStack gives precedence to the URL based id property, so my string gets the encoded value which is no use to RavenDb directly.
The options I'm aware of:
Change backbone to post to "/activities" and let the JSON Serialiser kick in
Change RavenDb ID generation to use hyphens rather than slashes
Make my Id property parse for the encoded %2F on set and convert to a slash
Both have disadvantages in that I either lose RESTfulness in my API, which is undesirable, or I don't follow RavenDb conventions, which are usually sensible out-of-the-fox. Also, I've a personal preference for having slashes.
So I'm wondering if there are any other options in servicestack that I could use to sort this issue that involve less compromise? Either Serialiser customisation or wildcard routing are in my head....
I have the same problem with ASP.Net WebAPI, so I don't think this is so much a ServiceStack issue, but just a general concern with dealing with Raven style id's on a REST URL.
For example, let's say I query GET: /api/users and return a result like:
[{
Id:"users/1",
Name:"John"
},
{
Id:"users/2",
Name:"Mary"
}]
Now I want to get a specific user. If I follow pure REST approach, the Id would be gathered from this document, and then I would pass it in the id part of the url. The problem here is that this ends up looking like GET: /api/users/users/1 which is not just confusing, but the slash gets in the way of how WebAPI (and ServiceStack) route url parameters to action methods.
The compromise I made was to treat the id as an integer from the URL's perspective only. So the client calls GET: /api/users/1, and I define my method as public User Get(int id).
The cool part is that Raven's session.Load(id) has overloads that take either the full string form, or the integer form, so you don't have to translate most of the time.
If you DO find yourself needing to translate the id, you can use this extension method:
public static string GetStringIdFor<T>(this IDocumentSession session, int id)
{
var c = session.Advanced.DocumentStore.Conventions;
return c.FindFullDocumentKeyFromNonStringIdentifier(id, typeof (T), false);
}
Calling it is simple as session.GetStringIdFor<User>(id). I usually only have to translate manually if I'm doing something with the id other than immediately loading a document.
I understand that by translating the ids like this, that I'm breaking some REST purist conventions, but I think this is reasonable given the circumstances. I'd be interested in any alternative approaches anyone comes up with.
I had this problem when trying out Durandal JS with RavenDB.
My workaround was to change the URL very slightly to get it to work. So in your example:
GET /api/users/users/1
Became
GET /api/users/?id=users/1
From jQuery, this becomes:
var vm = {};
vm.users = [];
$.get("/api/users/?" + $.param( { id: "users/1" })
.done(function(data) {
vm.users = data;
});
http:www.site1.com/?sid=555
I want to be able to have the sid parameter and value persist whether a form is posted or a link is clicked.
If the user navigates to a view that implements paging, then the other parameters in the querystring should be added after the sid.
http:www.site1.com/?sid=555&page=3
How can I accomplish this in Asp.Net Mvc 2?
[Edit]
The url I mentioned on top would be the entry point of the application, so the sid will be included in the link.
Within the application links like:
<%= Html.ActionLink("Detail", "Detail", new { controller = "User",
id = item.UserId })%>
should go to:
http:www.site1.com/user/detail/3?sid=555
This question is different than what Dave mentions, as the querystring parameter is persisting throughout the site.
Firstly, I'd say if the value needs to be persisted throughout the session then you should store it in Session and check that its still valid on each action call. This can be done through a custom action attribute you add to the controller / actions required. If the value is required then when the value is checked you can re-drect to a login page or similar if not present or its expired.
Anyway, that said I thought I would have a crack at getting it working. My first thought would be to create a custom action filter attribute which took the value of the querstring and stored it in session in OnActionExecuting and then OnResultExecuted would add the key back to the querystring. But as QueryString in Request is a read-only collection you can't do it directly.
So, whats now available to you?
Option #1 - Add it to all calls to Html.ActionLink() manually
or ...
Option #2 - Override a version of ActionLink which automatically adds the value for you. This can be achived like so. I wouldn't recommend doing this though.
Start off with the custom attribute.
public class PersistQueryStringAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var sid = filterContext.RequestContext.HttpContext.Request.QueryString["sid"];
if (!string.IsNullOrEmpty(sid))
{
filterContext.RequestContext.HttpContext.Session["sid"] = sid;
}
base.OnActionExecuting(filterContext);
}
}
All this does is check the request querystring for the required key and if its available add it into the session.
Then you override ActionLink extention method to one of your own which adds the value in.
public static class HtmlHelperExtensions
{
public static MvcHtmlString ActionLink<TModel>(this HtmlHelper<TModel> helper, string text, string action, string controller, object routeValues)
{
var routeValueDictionary = new RouteValueDictionary(routeValues);
if (helper.ViewContext.RequestContext.HttpContext.Session["sid"] != null)
{
routeValueDictionary.Add("sid", helper.ViewContext.RequestContext.HttpContext.Session["sid"]);
}
return helper.ActionLink(text, action, controller, routeValueDictionary, null);
}
}
On each of the action which is going to be called apply the attribute (or apply it to the controller), eg:
[PersistQueryString]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
Note
As the query value gets put into session it will be applied for the life of the session. If you want to check that the value is there and the same each request you will need to do some checking in the attribute overridden method.
Finally
I've purely done this as a "can it be done" exercise. I would highly recommend against it.
Possible Duplicate:
How do you persist querystring values in asp.net mvc?
I agree with the accepted answer to the question linked above. Querystring parameters are not designed for data persistence. If a setting (i.e. sid=555) is intended to persist through a session, use Session state or your Model to save that data for use across requests.