I am making a .NET 6 project, and I need to accomplish a task against an external API.
Let's define a model to set up the context.
class MyModel
{
public bool field1;
public string field2;
}
So, that model will be used in two actions.
First, a GET request, that can NOT contain some of the fields above, and
a POST request, that also can NOT contain some of the fields above.
For example:
// GET
{
MyModel: {
field1: true
}
}
// POST
{
MyModel: {
field1: false,
field2: "some value"
}
}
So, when I perform a GET operation, I want to include some particular fields. And send it two another external API, so I read from API1, and send it to API2.
When I perform a POST operation, I read from API2, and insert data into API1, but I just want to include some predefined fields.
My desire it's to create a custom attribute to reflect that behaviour:
class MyModel
{
[Read, Write]
public bool field1;
[Write]
public string field2;
}
Also note that a field can be used also for the GET operation, althought for the POST operation.
I am getting lost in how to implement the behaviour of the Newtonsoft.Json package to make it understand how to deserialize and serialize those specific fields based on an input operation.
My first approach it's to define a Read and Write interfaces, and when a request for write is made to the external service, it will serialize the fields that has a Read annotation to send the data, and when a request is made for read, the model will know how to deserialize the content into the attributes that has a Write.
As you may notice, concepts are inverted. Read means send data to the external API (because it will be read from another but internal API of the project), and Write means read data from the external API (because will be inserted in our internal API).
Thanks.
I would suggest to simply go for two models.
A Read Model
class MyReadModel
{
public bool field1;
}
And a Write Model
class MyWriteModel
{
public bool field1;
public string field2;
}
This makes it very clear.
Think of other situations: E.g. you want to generate a API Documentation with Swagger. You would also have to deal with your custom attributes.
In you controller you will have to methods:
[YourAllowAttribute]
[HttpGet]
public async Task<ActionResult<MyReadModel>> Get()
{
// stuff
}
[YourPostAttribute]
[HttpPost]
public async Task<ActionResult<MyReadModel>> Write()
{
// stuff
}
Security trimming can be achieved by implementing two attributes or policies e.g. [YourAllowAttribute] or [YourPostAttribute].
With this approach you will not mix concerns in your model.
Related
I have created a class to store data from API calls I am making. It returns JSON with some meta information, and then an array of data depending on the call being made. The meta information will always have the same fields, so I have created a "Root" class for this, but the data will be different depending on the call being made, so I have created different classes for each type of data, e.g. user data, company data, etc. As shown below, I currently have the "data" property set to a list of objects, but I am trying to figure out the best way to incorporate the different types of data that can be returned, since it will vary based on the call being made.
Right now I have the data saved as a list of objects, but I would like this to change depending on what data I am receiving. Like, if I am retrieving users, I would like for it to be a list of users.
What is the ideal way to accommodate for this? The only way I can think to do it now is to create a different "Root" class for every type of data I am expecting to receive, but that doesn't feel like it should be the most concise way to do it. I was looking into making this a factory design pattern but I wasn't sure that it fit this scenario.
Just use a generic base class:
public abstract class ApiCallResult<T>
{
// With your properties
// public int Limit { get; set; }
// [...]
//
public IEnumerable<T> Data { get; set; }
}
Then define a result per api call.
public class UserApiCallResult : ApiCallResult<User>
{
}
Created a small working example here:
dotnet fiddle
I have to build a .NET Core REST API and I have about two dozen endpoints that take in simple JSON objects like:-
{
"foo": 23,
"bar": "bar_value"
}
and
{
"foo": 12,
"baz": true
}
etc.
Some properties, such as foo above, are common among several endpoints but have different validation requirements. In some endpoints they are required, in others, they are not and so on. I can't change these JSON payloads as they're generated by a third party I don't have any control over.
How can I map these parameters to endpoints in a .NET Core API method directly, without a class?
I can, of course, create a class for each endpoint, such as
public class SomeObject
{
[Required]
[Range(0, 100)]
public int? Foo { get; set; }
public string bar { get; set; }
}
public class SomeOtherObject
{
public int? Foo { get; set; }
[Required]
public bool Baz { get; set; }
}
...
Note the different validation rules.
But I don't feel like creating some two dozen classes. I'd much rather just specify them directly in the endpoint method:
[HttpPut]
[Route("/some-route")]
public IActionResult SomeAction([Required, Range(0, 100)] int? foo, byte? bar)
{
...
}
[HttpPut]
[Route("/some-other-route")]
public IActionResult SomeOtherAction(int? foo, [Required] baz)
{
...
}
It would be much easier to read and figure out which property is required and when by just looking at the methods instead of opening one of two dozen similarly named class files or opening one single file with two dozen similarly named classes with properties of the same name.
So how can I get .NET Core to parse the JSON and assign the property values to the action method parameters?
I'm not aware of a direct answer to this question as specified, so I'll answer this with an alternative approach as an XY problem based on your statement "It would be much easier to read and figure out which property is required and when by just looking at the methods".
This assumes there's not an easy way document your own API surface area if you're using classes. In your example, you're already writing a large amount of logic in the method signature itself, not to mention potential behaviors for default values, etc., that can make those signatures progressively harder to read and understand, and that's exactly what input model classes and model validation are designed to help encapsulate. Furthermore, now that you've decomposed the model into its parts, it becomes increasingly complex to handle validation issues as a cohesive model, regardless of whether it could be done. By accepting the entire object at once, you can run a ModelState.IsValid check, aggregate errors, or add your own and quickly return that from the controller.
By adding XML documentation to your endpoint methods and input model classes, you also open up the easy path of adding a Swagger page with Swashbuckle, which will provide a simple way for you to inspect what the model value types are and which ones are required, etc., as well as example JSON bodies in the Swagger page itself with full documentation as to the purpose of all the parameters.
While you do end up with a bunch of model classes, it's just a button press away from Visual Studio to hop to your class and see your validation requirements and input types while "in code". If class generation is frustrating, you can quickly drop your JSON samples into a class generator online and get a "pretty good" starting point for the input models: https://json2csharp.com/
I have a method definition:
public async Task<IHttpActionResult> FindUsers([FromBody]User user)
This uses a class:
public class User
{
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I would like to extend the functionality of this without breaking the existing endpoint for current clients.
All clients make requests to the RESTful endpoint using an instance of System.Net.Http.HttpClient. They accept the JSON response and deserialize it into a list of User instances:
var user = JsonConvert.DeserializeObject<User>(content);
I would like to add a property to the User class:
public IList<string> Countries { get; set; }
I do not want this to break the endpoint for existing clients. That is, I want them to be able to continue deserializing into instances of their User class without problems.
I would also like them to be able to deserialize into an updated version of the User class if they wish to take advantage of the updated functionality.
The extended functionality would be implemented in the endpoint. It would detect if the Countries list has been provided in the request and, if so, perform a different operation and, thus, return a response - one which includes the Countries list.
Is this possible to do without breaking the endpoint for existing clients?
In my experience, adding data like this is generally not a problem.
If you send the data to the client, there is no need for them to expect or use it.
They may not have a property to deserialise countries into, but that shouldn't be a problem.
They don't see it, don't use it, etc.
The signature of your method is not changing (you are still basically expecting a User object) so everyone could still use the same endpoint. If they happen to supply countries, then they will be deserialised into your newer model. If they don't supply countries, then that property will not be set.
It is then up to you to decide what to do based on whether that data is provided.
As you might expect, this is a rough answer based on my own experience.
Your situation may be (is likely) to be more complicated than expressed by the question, but hopefully this provide some help.
I can use [FromBody] for single type , but is there any way to use multiple?
From the searching and reading I've done there is not a way, but i don't know if a way has been added in MVC 6.
If not, where would be best to start with custom way.
What should i be researching and looking for best method or place to hook in just before model binding so i can include my own method?
The best way is to create a composite wrapper:
public class Wrapper
{
public ModelA A { get; set; }
public ModelB B { get; set; }
}
Put Wrapper in the parameter list and mark that [FromBody]. You can't use that attribute more than once because all of the contents of the body are assumed to match the parameter type.
The composite wrapper approach is great if you can easily share your model between the caller and server, but a more generic approach is to use JObject:
using Newtonsoft.Json.Linq; // this gets you to JObject
[Route("svc/[controller]/[action]")]
public class AccountController : Controller
{
[HttpPost("{accountid}")]
public IActionResult UpdateAccount(int accountid, [FromBody]JObject payload)
{
var loginToken = payload["logintoken"].ToObject<LoginToken>();
var newAccount = payload["account"].ToObject<Account>();
// perform update
return this.Ok("success");
}
}
You could probably also go with a Tuple which would give you the concrete types specified in the parameter list. This would be great if your clients were always .NET apps (.NET 4.0 or later, specifically) but you'd probably want to write a helper library to get the right wire format if you were trying to come from AJAX or something similar.
I'm utilizing the Web API in MVC4 to provide a simple API for my web service. I have read the various posts on SO about using [XmlType], [XmlRoot], [XmlElement] etc. on the model to set custom names on the root-element and on the element wrapping each entity, but none of them seem to have an effect at all. The API works fine overall, but the XML-attributes seem to have no effect, leaving me with irrelevant element names.
I've simplified my code somewhat, to make the example more clear, but this is basically what it looks like:
API controller:
public class SomeController : ApiController
{
[HttpGet]
public List<MyViewModel> All()
{
return ApiProvider.All()
.Select(v => new MyViewModel
{
SomeId = v.SomeId
SomeValue = v.SomeValue
}).ToList();
}
}
MyViewModel:
public class MyViewModel
{
public int SomeId { get; set; }
public int SomeValue { get; set; }
}
Current XML-response:
<ArrayOfMyViewModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/...">
<MyViewModel>
<SomeId>3</SomeId>
<SomeValue>My value</SomeValue>
</MyViewModel>
</ArrayOfMyViewModel>
Desired XML-response:
<CustomRootName xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/...">
<CustomEntityName>
<SomeId>3</SomeId>
<SomeValue>My value</SomeValue>
</CustomEntityName>
</CustomRootName>
I have made no configuration changes to what serializer I'd like to use, or anything like that.
Am I missing something here, or why can I not change the name of the elements? Is there some other approach to take, or some necessary setting that I'm missing?
Can't you just have a class
CustomRootName : List<MyViewModel>
and return an instance of CustomRootName?
[my reply to your comment follows below]
Yes, that was my view when we started our api but ArrayOfMyViewModel is essentially a pseudo wrapper created by the serialiser and creating a one line wrapper per resource list was that much of a bind. Consider when your api gets more complex and you need to support more complex list types, ie:
<CustomRootName >
<RootType>typeB</RootType>
<CustomEntityName>
<SomeId>3</SomeId>
<SomeValue>My value</SomeValue>
</CustomEntityName>
<CustomEntityName>
...etc
</CustomEntityName>
</CustomRootName>
You would benefit from a consistent interface. Another benefit we found was with documentation. We document our code using xml-doc tags and then auto-generate the api user guide using xslt and a bit of reflection. If you don’t have a wrapper class then there is knowhere to hang the xml-doc tag. So initially it looks like a pain but I was soon convinced.