Flutter : Use generics for http response - http

I am new in Flutter.
I migrate from android development by Kotlin to Flutter.
I would like to get a response from Http Url.
All of the server's responses are in one format and the type of 2 variables is generic.
I did it in Kotlin by this 2 class.
class BaseResponse<T> {
#SerializedName("IsSuccess")
var isSuccess: Boolean = false
#SerializedName("Item")
var item: T? = null
#SerializedName("ListItems")
var listItems: List<T>? = null
#SerializedName("ErrorCode")
var errorCode: Int? = null
And other classes and responses are like this:
data class TablesModel(var id: Long, var tableType: String, var tableCost: Int, var banner: String?, var onLineCount: Int)
And my responses are like this :
BaseResponse<TablesModel>
Now I would like to do it in Flutter. Please help me.

For general json models in Dart is very common to use ValueObjects/Mojos.
You could use json_serializer and a builder to prepare the model classes yourself. Or a tool like
https://app.quicktype.io/ that will generate the model from you from a json response.
Regarding generics, the proper way is to have an abstract model class and implement the different responses types in subclasses, or you can leverage the Type system passing a argument so the compiler knows how to treat those properties.
The simplest approach would be to use the type dynamic or Object like jsonDecode doest, but you lose type checks for errors at compile time and autocomplete hints from the IDE.

Related

Provide a Converter for data-binding by defining a pair of SerializableFunction objects

In Vaadin 8 Framework, and Vaadin 10 Flow, the data-binding capability lets us provide a Converter to mediate between the widget’s expected data type (such as String for a TextField) and the data type of the backing bean property (such as Integer number).
In this example, the built-in Converter implementation StringToIntegerConverter is used.
binder
.forField( this.phaseField )
.withConverter(
new StringToIntegerConverter( "Must enter an integer number" )
)
.bind( Panel::getPhase , Panel::setPhase ) ;
But what about defining a Converter for other types? How can I easily define a short-and-sweet Converter? For example, a String-to-UUID converter. I want to show the canonical 36-character hex string in a TextField, and going the other direction, parse that string back into a UUID.
// String to UUID
UUID uuid = UUID.fromString( myString ) ;
// UUID to String
String myString = uuid.toString() ;
I see that Binder.BindingBuilder offers the pair of methods withConverter that both take a pair of SerializableFunction objects.
Binder.BindingBuilder::withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation)
Binder.BindingBuilder::withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation, String errorMessage)
➥ So how do I define the pair of SerializableFunction objects/classes?
I noticed that this interface lists a known subinterface ValueProvider<SOURCE,TARGET>. That looks familiar, and I have a hunch it is the key to easily defining a short simple converter. But I do not quite comprehend the syntax with lambdas and all that is going on here.
I am not asking how to write a class implementing Converter. I am asking how to write the pair of SerializableFunction arguments to pass to the Binder.BindingBuilder::withConverter methods listed above as bullet items.
Quoting that JavaDoc:
Interface Binder.BindingBuilder<BEAN,TARGET>
…
withConverter
default <NEWTARGET> Binder.BindingBuilder<BEAN,NEWTARGET> withConverter(SerializableFunction<TARGET,NEWTARGET> toModel, SerializableFunction<NEWTARGET,TARGET> toPresentation)
Maps the binding to another data type using the mapping functions and a possible exception as the error message.
The mapping functions are used to convert between a presentation type, which must match the current target data type of the binding, and a model type, which can be any data type and becomes the new target type of the binding. When invoking bind(ValueProvider, Setter), the target type of the binding must match the getter/setter types.
For instance, a TextField can be bound to an integer-typed property using appropriate functions such as: withConverter(Integer::valueOf, String::valueOf);
Type Parameters:
NEWTARGET - the type to convert to
Parameters:
toModel - the function which can convert from the old target type to the new target type
toPresentation - the function which can convert from the new target type to the old target type
Returns:
a new binding with the appropriate type
Throws:
IllegalStateException - if bind has already been called
You can do it by passing two lambda expressions to withConverter, so something like this:
binder.forField(textField)
.withConverter(text -> UUID.fromString(text), uuid -> uuid.toString())
.bind(/* ... */);
If you need a more complicated conversion, then the right-hand side of the lambda can be surrounded with brackets, e.g.
binder.forField(textField).withConverter( text -> {
if ( text == null ) {
return something;
} else {
return somethingElse;
}
}, uuid -> { return uuid.toString(); } )
.bind(/* ... */);
If you need your converter multiple times, I recommend creating a separate class implementing interface com.vaadin.data.Converter. However, using lambdas is possible, too, as you already know (see answer of #ollitietavainen). But this is not Vaadin specific, it's a Java 8+ feature you can read about e.g. here. Basically, you can use lambdas whereever an object implementing an interface with only one method is required.

Angular2 HTTP Post ASP.NET MVC Web API

How do you properly create a Web API POST of complex object or multiple parameters using Angular2?
I have a service component in Angular2 as seen below:
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin', JSON.stringify({ Email: inputEmail, Password: inputPassword}), this.options);
}
The targeted web api is seen below:
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(string email, string password)
{
....
}
This does not work because I need to convert the parameters of the web api into a single POCO class entity with Email and Password properties and put the [FromBody] attribute: Signin([FromBody] Credential credential)
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
Because what if I have numerous Web API POST actions with parameters like (string sensitiveInfo1, string name, int sensitiveInfo2) or (ClassifiedInfo info, string sensitiveInfo1, string sensitiveInfo2), do I need to convert them all to POCO classes and always use [FromBody]?
PS.
I was using RestangularJS before and it can posts anything (mulitple primitive objects and complex objects) without my Web API actions having [FromBody] attributes. Will about to investigate how RestangularJS do it.
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
I know its not what you want to hear but out of the box this is not possible. It is not a limitation of the browser code that is making the request. This means it does not matter if you are using Angular, JQuery, straight JavaScript, or even RestangularJS. This is a limitation (I use that word loosely as I am sure this is by design) of Web API (any version). Here is the documentation on this design: Parameter Binding in ASP.NET Web API by Mike Wasson.
At most one parameter is allowed to read from the message body. So this will not work:
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
So the question becomes, what are your options?
Create a model
This is the thing you were trying to avoid but I list it first because this is how Web API was intended to behave. I have not yet heard a compelling reason not to do this. This approach allows you to extend your model easily without having to change the method signature. It also allows for model validation on the model itself. Personally I really like this approach.
public class SignInModel{
public string Email {get;set;}
public string Password {get;set;}
}
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(SignInModel signInModel)
{
// ....
}
I did not repeat your existing JavaScript code because what you have works as is with the above web api code
URL
Again, what you were trying to avoid. This does make what you want possible with the limitation that you have to pass these parameters using the Query string on the URL. The JavaScript would change but the signature you had on the Web API method would not.
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin/?email=inputEmail&password=inputPassword', null, this.options);
}
I did not repeat your existing Web API code because what you have works as is with the above web JavaScript code (by default FromUri is assumed I believe)
Custom Model Binder
See Passing multiple POST parameters to Web API Controller Methods by Rick Strahl. This option allows you to create a custom model binder that could do what you are asking. It is a whole bunch of extra code though for, IMHO, not much benefit. Maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
Dynamic
Finally you could also pass in a dynamic object as the parameter of your Web API. This is essentially the same as receiving the JSON as a string and making your Controller code responsible for the deserialization of content. Again, I believe that this would make your code worse in most situations as you have to implement custom validation and type checks. This answer was proposed previously on SO by Bes Ley. Again, maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
If you call Web API 2.2 post method from Angular 2 type script, dont forget to add following header content and parameter object.
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var params = new URLSearchParams();
params.set('userid', '102');
params.set('username', 'foo');
return this._http.post('http://localhost:6579/api/PostUser', params.toString(), { headers: headers }).map(res => res.json());
Perhaps you should post with options:
{
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
and encode data like
jQuery.param({user:'bla', password: 'bla'});
WebAPI does not provide this out of the box. If you try to get understanding of web API bindings, you might be able to figure out why.
I think this article might help.
The generic rules are:
– simple, string-convertible parameters (value types, strings, Guids, DateTimes and so on) are by default read from URI
– complex types are by default read from the body
– collections of simple parameters are by default read from the body too
– you cannot compose a single model based on input from both URI and request body, it has to be one or the other
I have fixed the issue of Angular2 HTTP Post ASP.NET MVC Web API
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
let params: URLSearchParams = new URLSearchParams();
params.set('value', '2');
let options = new RequestOptions({
headers: headers//,
//search: params
});
let content = new URLSearchParams();
content.set('StudentName', 'Inderjit Singh';
content.set('Mobile', '+919041165398');
content.set('Nationality', 'Indian');
content.set('AdmissionNo', '6');
content.set('SectionCode', '1');
content.set('Gender', 'Male');
content.set('RegNo', '18585');
content.set('ClassCode', '1');
this.http.post('YOUR_URL', content.toString(), { headers: headers }).map((res: Response) => { console.log("data is==>" + res.text()); }).subscribe();
WebApi will be able to deserialize your Credential object provided the JSON object has the same field names (I am not sure about case so you may be right here). You seem to be missing the headers from the post call in your Angular2 component.
Can you check the Content-Type using Chrome Debugger or Fiddler? It should be application/json.
Try this, passing a complex class object into a single data parameter.
var SearchQuery = function () {
this.Alphabet = null;
this.Search = false;
this.Keyword = null;
this.RegionList = null;
};
var para = new SearchQuery();
{ data: JSON.stringify(para) } - Post Data
you can receive it using a JObject in your API controller and deserialize it as according to your classes.

Is there any disadvantage to using a dynamic return type on my API endpoints?

I am currently building a web api service using MVC and I am creating the endpoints. For example, my GET endpoint will execute a stored procedure and return the data in JSON format. The model of the data returned can vary in the future and it seems like using a dynamic return type would remove the need of having to change the model and mapping every time that happens. Basically, here is some sample code, do you notice any malpractices in my implementation?
[System.Web.Mvc.HttpGet]
[Route("companies/{id}")]
public dynamic GetCompany([FromUri] int id, string userId)
{
var parameters = new Hashtable
{
{"UserID", userId},
{"CompanyID", id}
};
var result = MyDB.ExecuteSp(CompanyReadByIdSp, parameters);
return result;
}
In fact, this would enable me to transform the object and add whatever I want to it without needing to worry about the model. Is this a bad way of doing things? Thanks ahead.

Ienumerable of T, T is only available at runtime

I have a method with this signature
public IEnumerable<T> GetAll<T>() where T : new()
{
// Orm Lite Version
return Connection.LoadSelect<T>();
}
At compile time I don't know the Type T. I only know the class name at runtime is it possible to call this method using reflection with something like this?
string TargetTBLName = ...;//TargetTBLName get's it's value at runtime
Type ParentTableClass = Type.GetType(TargetTBLName);
IEnumerable<Type.GetType(TargetTBLName)> test = Repository.GetAll<Type.GetType(TargetTBLName)>();
Any Ideas?
I'd give a Dynamitey library a go. You can do that in many different ways, but I prefer this one because of its simplicity. You can find it here.
var name = InvokeMemberName.Create;
var test = Dynamic.InvokeMember(Repository, name("GetAll", new[]{ParentTableClass }));
foreach(var obj in test)
{
obj.SomeMethodFromMyType();
}
Keep in mind, that if Repository is static, than you have to tweak it a bit to use static invocation context (look up the link).
Now you have a test object, which is a dynamic - you can use it with duck typing (which has some implications on speed, for example), but in general you can do whatever you want to do with a normal IEnumerable<YourType>.
If you want to use reflection:
MethodInfo getAll= typeof(Repository).GetMethod("GetAll");
MethodInfo getAllGeneric= getAll.MakeGenericMethod(ParentTableClass);
object result = getAllGeneric.Invoke(this, null);
//or null, null is Repository is static
var finalObject = result as IEnumerable;
Mind that since this ParentTableClass is an unknown during compilation, you won't have access to anything that the actual type provides - unless you use dynamic approach.

How can I let Rebus determine an endpoint for an interface and not for an implementation of that interface

When I Send an instance created by AutoMapper.Mapper.DynamicMap() I run into an error where Rebus seems to want to determine an endpoint for proxy type returned by DynamicMap. This instance will contain an implementation of T assuming T is an interface type.
Is there a way in which I can let Rebus determine an endpoint for the interface type T and not for the implementing type returned by DynamicMap()?
I tried playing around with IDetermineMessageOwnership but had no luck so far.
public static void Send<T>(this IBus bus, object source)
{
var message = Mapper.DynamicMap<T>(source);
bus.Send<T>(message);
}
Thanks in advance!
Sure! In your case, you could create your own implementation of IDetermineMessageOwnership which is the service that Rebus uses to map a message type to an endpoint.
If you want to leverage Rebus' existing logic, you could decorate any chosen strategy and extend it with a keep-looking-at-all-implemented-interfaces-until-one-can-be-mapped strategy like so:
Configure.With(adapter)
.(...)
.MessageOwnership(o => o.FromRebusConfigurationSection())
.Decorators(d => d.AddDecoration(DecorateOwnershipMapping)))
.CreateBus()
.Start();
where DecorateOwnershipMapping would install a decorator on top of whatever is configured like so:
void DecorateOwnershipMapping(ConfigurationBackbone b)
{
b.DetermineMessageOwnership = new CustomizedEndpointMapper(b.DetermineMessageOwnership);
}
and a possible implementation could look like this:
class CustomizedEndpointMapper : IDetermineMessageOwnership
{
readonly IDetermineMessageOwnership wrappedEndpointMapper;
public CustomizedEndpointMapper(IDetermineMessageOwnership wrappedEndpointMapper)
{
this.wrappedEndpointMapper = wrappedEndpointMapper;
}
public string GetEndpointFor(Type messageType)
{
var mappingCandidates = new[] {messageType}
.Concat(messageType.GetInterfaces())
.ToList();
foreach (var typeToTry in mappingCandidates)
{
try
{
return wrappedEndpointMapper.GetEndpointFor(typeToTry);
}
catch{}
}
throw new InvalidOperationException(string.Format("Could not determine the owner of message of type {0} - attempted to map the following types: {1}",
messageType, string.Join(", ", mappingCandidates)));
}
}
thus iterating through the concrete type as well as all inherited interface types when trying to determine the owning endpoint.
In your case, I believe this would work flawlessly when determining the message owner. Only problem is that the serializer will most likely complain, because the dynamically generated type cannot be recognized again when the message is received.
Hence, this trick requires customization of the serializer as well. If you're using the (default) JSON serializer, you might get away with some custom resolvers like so:
Configure.With(...)
.(...)
.Serialization(s => s.UseJsonSerializer()
.AddNameResolver(CustomNameResolver)
.AddTypeResolver(CustomTypeResolver))
.(...)
where CustomNameResolver and CustomTypeResolver are methods that must then take care of mapping the type to a type name and mapping the type name to a type that can then be deserialized into. In order to make this work with AutoMapper, you'll probably need to either
a) somehow use AutoMapper to look up the type of the received message and return that type from CustomTypeResolver, or
b) customize the serializer to have AutoMapper somehow participate in generating the object to be returned
but I must admit that I'm unsure whether the last part will play out smoothly.
A final note: if you succeed in making this work, I suggest you pack up your configuration spells into a reusable extension method on RebusConfigurer so that your endpoints can just go
Configure.With(...)
.(...)
.UseMyFunkyAutoMapperMessagesJustTheWayILikeIt()
.CreateBus().Start();
in all your Rebus endpoints...
I'll appreciate it if you'll let me know how this works out for you! :)

Resources