A long time ago when I first looked at OData response payloads to GET requests contained links to other entities or entity sets (e.g. an OrderHeader entity would contain a link to the order's OrderDetails). I believe the correct term for this is hypermedia.
Today I'm checking out OData again and have built a OData v4 service using ASP.Net Web API however no such hypermedia links are being returned in the payloads. Why is this? Is it because the payload is now JSON (whereas when I looked years ago it was XML)? Is there any way to include hypermedia links in the payload?
Here's what I've built. I have an entity called Proposition:
public class Proposition
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Event> Events { get; set; }
}
Notice that a Proposition has a collection of Events. I was hoping that when I requested a Proposition via the OData endpoint that I would get a link to the Events resource, but I don't:
I've found some information at OData JSON Format Version 4.0 Plus Errata 02#JSON Format Design that suggests adding $format=odata.metadata=full to the URL will return what I need:
The odata.metadata=full format parameter indicates that the service MUST include all control information explicitly in the payload
(odata.metadata=full)
but I've tried that and I'm not getting back any such metadata (screenshot this time from Postman):
Postman shows me that what got returned was odata.metadata.minimal:
Why is it ignoring my request for full metadata?
You should be able to add $format=application/json;odata.metadata=full to your querystring to achieve this via a GET request.
Aha, nailed it. I was specifying the URL wrongly. It needs to be ?$format=application/json;odata.metadata=full
Thanks to Stuart Preston for the help: https://twitter.com/StuartPreston/status/601107122550616064
Related
I'm developing .net core ODATA web api application.
I have EF entity Operation with Json serialized property PaymentProfile.
I have problem with getting Operation.PaymentProfile.PaymentMethods property via ODATA request
Configuration of Operation fluent API:
modelBuilder.Entity<Operation>().Property(e => e.PaymentProfile).HasConversion(
v => JsonConvert.SerializeObject(v, JsonSerializerSettings),
v => JsonConvert.DeserializeObject<PaymentProfile>(v, JsonSerializerSettings));
PaymentProfile is entity with other properties like:
public ICollection<PaymentMethod> PaymentMethods { get; set; } = new List<PaymentMethod>();
public CascadeStrategy CascadeStrategy { get; set; }
When i get request with expan of CascadeStrategy, it works fine:
odata/Operations?$expand=PaymentProfile($expand=CascadeStrategy)&$top=1&skip=0
Server return correct odata objects
But if i expand PaymentMethods property, it return corruped data with 200 http status.
Request:
odata/Operations?$expand=PaymentProfile($expand=PaymentMethods)&$top=1&skip=0
Response:
{"#odata.context":"http://localhost:5020/odata/$metadata#Operations(PaymentProfile(PaymentMethods()))","value":[
If i request PaymentProfile with expand PaymentMethods, it works fine too:
odata/PaymentProfiles?$expand=PaymentMethods&$top=1&skip=0
Operation and PaymentProfile are from different EF context.
How can i fix it and get Operation.PaymentProfile.PaymentMethods property? Any ideas?
UPD: "currepted data" means response is not finished and is a not valid JSON. Lookse like its only first 112 symbols of true response
Did you set the Accept header when requesting CascadeStrategy while PaymentMethods didn't?
Accept: application/json;odata.metadata=none
In general, the request results have #odata.context, which is what you call corrupted data. You can check this document.
If you don't want it to appear, you can add Accept header like above, you can also add $format=application/json;odata.metadata=none to the query string, which will remove odata metadata from the results:
odata/Operations?$format=application/json;odata.metadata=none&$expand=PaymentProfile($expand=PaymentMethods)&$top=1&skip=0
I have implemented a logic for Response in my Web API projects, and response object looks like this:
public class Response
{
public bool IsSuccess { get; set; }
public object Data { get; set; }
public string Message { get; set; }
}
IsSuccess indicates whether the call is success or failure.
Data to be used for further processing when IsSuccess is true
Message to be used for further processing when IsSuccess is false
I have few questions in mind. Please help me with this.
Is it ok to use custom objects over the status codes?
What disadvantages this approach will it have?
Should I switch to status codes / ControllerBase return methods instead?
HTTP has standardized the structure for representing request and response. To that extent, a response has 3 parts - Status, Headers & Body. Please refer here. Each part has a definite purpose. Since the question is on status codes, I'll restrict myself to it.
The primary purpose of status codes is to indicate whether the request has been processed correctly or not. The automation systems and scripts depend on it for branching their decisions.
It is important to remember that the model defined will be part of the response body. This means that the framework API is built on will still include a default response code - usually, it's a 200 OK. If the IsStatus attribute is supposed to act as a replacement for the Status code, if proper care is not taken, the status code and IsStatus may show different values when the API errors out.
Finally, I think you are better off representing an ErrorResponse instead. Something in the lines of -
public class ErrorResponse{
// Application or Service specific code
// to identify the error
public string Code {get; set;}
// A link to detailed description of the
// of the error
public Uri Info {get; set;}
// A high level friendly message about the
// error
public string Message {get; set;}
}
HTTP status codes are a standard. See e.g. this docu. Nobody is expecting to get a 200 OK with IsSuccess set to false, because 200 OK is a success. 3xx are redirects, 4xx are client errors, 5xx are server errors. Stick to that, do not reinvent the wheel, you'll confuse the clients of your API.
However, you can and it's a good practice to include more custom information into your response. Define your response e.g. like this:
public class ErrorDetails
{
public string Message { get; set; }
}
Than set the response code directly on the response object of .net, not on your own:
var error = new ErrorDetails { ... };
context.Response.StatusCode = 4xx / 5xx; // and not 200, it's an error! context is HttpContext
await context.Response.WriteAsync(JsonConvert.SerializeObject(error));
Controller methods already have build-in methods for this, so no need to do it the hard way as in the example above either. E.g.:
this.BadRequest(error);
This will set a 404 on the response object and pass your error object in the payload as additional data. There is this.Ok() and a bunch of other methods for each situation.
I would like to write method validation processes which will be similar to data annontations presented in Web API.
In web api we can validate an object, for example:
public class Numbers
{
[NumberOne]
public string Number1 { get; set; }
[NumberTwo]
public string Number2 { get; set; }
}
and as long as we define the attributes NumberOneAttribute and NumberTwoAttribute its gonna be ok.
The difference is that web api has access to the GlobalConfiguration.Configuration.Filters which it seems like signalr doesn't.
Is there anyway to validate requests by attributes? or I need to follow the worst case, validate each input in the invoked method?
Thanks,
Ori.
In SignalR 2.2.x there is no native way of achieving this, but there is a project on GitHub that that adds a Validation Module in the SignalR pipeline.
Basically, in order to use it, you add a new module to the pipeline:
GlobalHost.HubPipeline.AddModule(new ValidationModule());
Then, you can use attributes like [Required] for the models' properties and then decorate the desired methods with the [Validate] attribute.
Note that this is a proof of contept project.
Best regards!
I have a working Web API that i am converting to a .Net Azure Mobile Service. The API returns a complex model - objects with properties - some of which are collections of other objects. This works as expected with plain Web API but with Azure Mobile Services I have an issue where one of my models does not have all it's properties serialized.
When i set a break point on the return statement in the controller, I see that all the properties and their values are present. This leads me to believe that the issue is with serialization (JSON).
return Request.CreateResponse(HttpStatusCode.OK, myModel);
Examples of properties that are being serialized:
public Guid Id { get; set; }
public IEntityDto ModelDto { get; set; } //this is an object with many properties all of which serialize
Examples of properties that are NOT being serialized:
public ItemStatus Status { get; set; } //this is an enum
public string Message { get; set; }
public string TestProp { get; set; } //this is a simple string property I added to help debug
How can I go about further debugging this so that i can see why these properties are being excluded?
Note: At the moment I am still running this locally not off Azure. This is with Visual Studio 2013 Update 2 RTM.
UPDATE: Upon closer inspection it appears that the properties not being serialized are properties that are either enums or have a value of null.
As #carlosfigueira mentioned in a comment to the original question, the default behavior of the JSON serializer is to exclude properties with null and default values. To address this I changed the following settings:
httpConfig.Formatters.JsonFormatter.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Include;
httpConfig.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
...where httpConfig is of type HttpConfiguration. You can make these changes on app start - in a config file like WebApiConfig.cs or directly in Global.asax.cs.
Assuming a ApiController with an action having multiple arguments:
[HttpPost]
public void AddAddress( Person person, Address address, int simple )
Or something like that.
Now I try to send a post request with content type application/json and json like this:
{
person: {...}, address: {..}, simple: 1
}
just assume it's valid json ;)
or in a json array like this:
[person, address, simple]
But WebApi won't recognize this and tell me it can't find the action, as it doesn't support multiple parameters in a json request.
I'm doing this from a C# client using HttpClient and we'd like to do post requests using json. I wanted to use the WebApi.Client package, which provides several useful extensions to the HttpClient, but we have a portable library project(which webapi client doesn't support).
How would I go about supporting this feature?
I tried to use an custom ActionFilter, which never get's to run as it can't find the action beforehand. I tried MediaTypeFormatter which doesn't know the context and the arguments, and also I tried ModelBinder, which also seems only on a per argument basis and doesn't always get executed.
Thanks!
The built-in parameter binding strategy in ASP.NET Web API with formatters only supports a single complex argument mapped to the request body. In your case, you are trying to map at least two complex arguments. The simplest solution without extending this infrastructure would be to create a new model class containing those arguments as properties
public class AddressModel
{
public Person Person { get; set; }
public Address Address { get; set; }
public int Simple { get; set; }
}
And use that model in your controller action
public void AddAddress( AddressModel model );