Why is there no compile error since the method only returns in the "if" block? - oop

I am working with Dart in Flutter right now, and in a tutorial I came across this method:
Future getData() async {
http.Response response = await http.get(url);
if (response.statusCode == 200) {
String data = response.body;
return jsonDecode(data);
} else {
print(response.statusCode);
}
}
Why is there no compile error? In Java or C++, there would be an error because if statusCode wasn't 200, the method wouldn't return anything. Is it because Future can act like a void type? Just don't really understand what is going on.

Yes,
When you define a "Future" it automatically defaults to
Future<void> or Future<dynamic>
If you want to force it to a certain return type you must declare it like so.
Future<int> or Future<double>
You get the idea.
However Dart doesn't operate like Java or C++, if you create a function that is supposed to return a value, but in a certain if, else clause there is no return clause, then the function will return null.

Related

What does ForbiddenContext.Fail() do?

When setting up my JwtBearerEvents there is an event called OnForbidden. That passes an object of type ForbiddenContext. ForbiddenContext has a method on it called Fail():
options.Events = new JwtBearerEvents()
{
// Other stuff here
OnForbidden = context =>
{
context.Fail("Bad Stuff");
return Task.CompletedTask;
}
};
The help for that method says:
Indicates that there was a failure during authentication.
But calling that method has no effect on the output of my application.
For my application, if I don't pass the tests in my custom IAuthorizationRequirement then a 403 is returned. As I said above, if I call ForbiddenContext.Fail() then the result is the same (a 403 with an empty body).
So, I am left wondering, what is the expected use of ForbiddenContext.Fail()?
The value will be saved to AuthenticateResult.Failure. If we want to get the output or know how to use.
Following is the demo code:
return context.Response.WriteAsync(
JsonConvert.SerializeObject(new ProjectResponse
{
StatusCode = (int)HttpStatusCode.Forbidden,
Message = context.Result.Failure.Message }
));

How to fetch initial data using provider in flutter effectievly

Recently, i did a flutter course.
The instructor was making the get request from an API so difficult. For a hybrid framework like flutter i never thought it's so difficult.
below are my code. I am using provider for state management.
Future<void> fetchAndSetProducts() async {
try {
const url = 'fetch-url';
final response = await http.get(url);
final data = json.decode(response.body) as Map<String, dynamic>;
final List<Product> loadedProducts = [];
data.forEach((key, value) {
loadedProducts.add(Product(
id: key,
title: value['title'],
description: value['description'],
imageUrl: value['imageUrl'],
price: value['price'],
isFavorite: value['isFavorite'],
));
});
_items = loadedProducts;
notifyListeners();
} catch (error) {
throw (error);
}
}
And in the products overview screen were I am showing the products page this method is called like below:
bool _isInit = true;
bool _isLoading = false;
#override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<Products>(context).fetchAndSetProducts().then((_) => {
setState(() {
_isLoading = false;
})
});
}
_isInit = false;
super.didChangeDependencies();
}
The other method included a sneaky way of using duration of zero just like we use in javascript set timeout giving a zero time.
It's worth noting that in didChangeDependencies we could not use async await, so most probably a call back hell awaits.
Also a variable needs to be initialized just for calling the api once upon loading.
Is there no easy solution to this? Or an industry way of dealing with this?
here is a minimal working example of what you can do, it's not the best thing in the world, but this is what works for me, let me know if you can make it any better.
The answer to your problem is really simple, BUT, you need to rearrange some stuff first.
A Flutter app can be split into multiple layers which are (just for example) data, state management and UI, in the data layer you will have all methods that communicate with the API, and you call them inside the state management solution (which is provider in your case), the result will be accessible from the provider which will save the data in a variable, then the UI will be able to retrieve these data from the provider, this seems a bit redundant I know, but there is a reason why we do that, if you put the API call inside the provider itself, and there is somewhere else in your app that uses the same endpoint then you will have duplicate code, as for the provider, it's the place where your data is stored in the runtime, these data are what makes the state of your app, finally, the UI can handle displaying data from the provider easily, just make a boolean in the provider that indicates if the API call is executing/loading or not, and inside the consumer in the UI display different widgets based on the boolean.
If we were to visualize the flow of the operation it would be like this:
1- action from the UI that triggers a method from the provider.
2- inside the provider method you will set the boolean indicating that the API call is executing to true and call notifyListeners().
3- call the API request and call .then() on it.
4- inside the .then() set the boolean to false to notify that the call is over and set the received data to a variable inside the provider and call notifyListeners again.
5- in the UI you should have a consumer listening to your provider and handling the boolean, if its true then display a CircularProgressIndicator for example, and if it's false then display your desired widget
Regarding the context in the initState you can fix this problem in 3 ways:
1- using WidgetsBinding.instance
.addPostFrameCallback((_) => yourProviderFunction(context));
2- by registering your provider in a service locator so you don't have to use a context at all. (which is what I used in the example project I posted above)
3- by executing the desired function in the constructor of the provider, so when its initialized the API request will be called
Is this the Academind course?
Also this is the correct way.
For using a Provider you need the context.
EDIT: Added BaselAbuhadrous' comment to the answer.
You need to use didChangeDependencies because the initState actually provides the context, but the screen layout isn't built yet, so you get an error, but if you used WidgetsBindings.instance and call the provider inside of it, then you won't get the error.
//your model , product_model.dart
import 'dart:convert';
List<Product> availableTicketsFromJson(String str) => List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));
class Product {
String title;
String description;
String imageUrl;
double price;
bool isFavorite;
Product(
{this.title,
this.description,
this.imageUrl,
this.price,
this.isFavorite});
factory Product.fromJson(Map<String, dynamic> json) => Product(
title: json['title'] as String,
description: json['description'] as String,
imageUrl: json['imageUrl'] as String,
price: json['price'] as double,
isFavorite: json['isFavorite'] as bool,
);
}
//viewmodel class
final String url = "test.com";
Future<List<Product> fetchProducts() async {
List<Product> products = List<Product>();
try {
final request = await http.get(url);
if(request.statusCode == 200) {
products = productsFromJson(request.body.toString());
notifyListeners();
} else {
print(request.statusCode.toString());
}
} catch(e) {
return List<Product>();
}
return products;
}
//fetch_data.dart
Create your instance of provider in the page that you wanna fetch the data:
Under State<yourWidget>
=> FetchDataViewModel _model;
List<Product> products = [];
under build method
=> _model = Provider.of<FetchDataViewModel>(context,listen: false);
Make a http request with FutureBuilder
FutureBuilder(future:_model.fetchProducts()),
builder: (context,snapshot)){
if(snapshot.connectionState == ConnectionState.done) {
products = snapshot.data;
if(products.length > 0) {
return ListView.builder(
itemCount: products.length,
itemBuilder : (context,index) {
return _items();
}
);
} else {return _noSavedDataWidget();}
}
}
You can test such a code
sometimes
Provider.of<'providerClassName'>(context, listen : false).'providerFunction'
might help.

Is there any concern about returning generic type object in Dart?

I want to implement a different error handling approach in a project without chaining exceptions.
To make it simple as possible, I am tend to write my own basic either-like model.
class Either<F, T> {
final F failure;
final T value;
const Either(this.failure, this.value);
Object check (){
if (failure != null) return failure;
return value;
}
}
I am concerning about returning the type Object, is there any problem or considerations with that in Dart or any other language?
Edit:
or returning dynamic type...
dynamic check(){
if (failure != null) return failure;
return value;
}
I think in your case, it's kind of a wired implementation. The question is, what do you want to do with the actual implementation ? Do you want to replace an if else that will appear over and over? In that case, what would you do if you have to handle the error (failure) ? I think a better approach is to use functions as parameters. Here's a short suggestion.
class Either<T, F> {
T value;
F fail;
Either(this.value, this.fail);
void check(success(T value), {failure(F fail)}) {
if (fail != null && failure != null) {
failure(fail);
} else if (value != null) {
success(value);
}
}
}
class SomeClass {
void checkTheImplementation() {
Either<String, Error> maybeString = Either("testing", null);
// if you don't want to handle the error.
maybeString.check((value) => print(value));
// if you want to handle the error
maybeString.check((value) => print(value), failure: (err) {
print(err.toString());
});
}
}
I have looked over and decided to go with baihu92's either_type way. It's much more clear and comprehensible than either in the dartz package. Here is my implementation:
and the usage is like:

Async Await Return Task Null .Net Core

I've a method like this:
public async Task<Response> HandleRequest(string connectionId, Request request)
{
if (request is AuthorizeRequest)
{
return await _handler.HandleRequest(connectionId, request as AuthorizeRequest);
}
if (request is ChangeConfigurationRequest)
{
return await _handler.HandleRequest(connectionId, request as ChangeConfigurationRequest);
}
return await Task.FromResult<Response>(null); //My question is here
}
My question is: Should I return return await Task.FromResult(null); or retun null;
Because if request is not 'AuthorizeRequest' and 'ChangeConfigurationRequest'
Tks all for helps
Since your method is marked as async, you should return null.
I search from many resources and it said that we should not return null in Task. Is it right?
Methods that return a task should never return a null task. However, what you want to do is return a (non-null) task that contains a result value of null. That's fine; it's completely different than returning a null task.
For your original code, use return null;. Do not ever use await Task.FromResult(...).

Lagom http status code / header returned as json

I have a sample where I make a client request to debug token request to the FB api, and return the result to the client.
Depending on whether the access token is valid, an appropriate header should be returned:
#Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, String>> login() {
return this::loginUser;
}
public CompletionStage<Pair<ResponseHeader, String>> loginUser(LoginUser user) {
ObjectMapper jsonMapper = new ObjectMapper();
String responseString = null;
DebugTokenResponse.DebugTokenResponseData response = null;
ResponseHeader responseHeader = null;
try {
response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
responseString = jsonMapper.writeValueAsString(response);
} catch (ExecutionException | InterruptedException | JsonProcessingException e) {
LOG.error(e.getMessage());
}
if (response != null) {
if (!response.isValid()) {
responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
} else {
responseHeader = ResponseHeader.OK.withStatus(200);
}
}
return completedFuture(Pair.create(responseHeader, responseString));
}
However, the result I get is:
This isn't really what I expected. What I expect to receive is an error http status code of 401, and the json string as defined in the code.
Not sure why I would need header info in the response body.
There is also a strange error that occurs when I want to return a HeaderServiceCall:
I'm not sure if this is a bug, also I am a bit unclear about the difference between a ServerServiceCall and HeaderServiceCall.
Could someone help?
The types for HeaderServiceCall are defined this way:
interface HeaderServiceCall<Request,Response>
and
CompletionStage<Pair<ResponseHeader,Response>> invokeWithHeaders(RequestHeader requestHeader,
Request request)
What this means is that when you define a response type, the return value should be a CompletionStage of a Pair of the ResponseHeader with the response type.
In your code, the response type should be String, but you have defined it as Pair<ResponseHeader, String>, which means it expects the return value to be nested: CompletionStage<Pair<ResponseHeader,Pair<ResponseHeader, String>>>. Note the extra nested Pair<ResponseHeader, String>.
When used with HeaderServiceCall, which requires you to implement invokeWithHeaders, you get a compilation error, which indicates the mismatched types. This is the error in your screenshot above.
When you implement ServerServiceCall instead, your method is inferred to implement ServiceCall.invoke, which is defined as:
CompletionStage<Response> invoke()
In other words, the return type of the method does not expect the additional Pair<ResponseHeader, Response>, so your implementation compiles, but produces the incorrect result. The pair including the ResponseHeader is automatically serialized to JSON and returned to the client that way.
Correcting the code requires changing the method signature:
#Override
public HeaderServiceCall<LoginUser, String> login() {
return this::loginUser;
}
You also need to change the loginUser method to accept the RequestHeader parameter, even if it isn't used, so that it matches the signature of invokeWithHeaders:
public CompletionStage<Pair<ResponseHeader, String>> loginUser(RequestHeader requestHeader, LoginUser user)
This should solve your problem, but it would be more typical for a Lagom service to use domain types directly and rely on the built-in JSON serialization support, rather than serializing directly in your service implementation. You also need to watch out for null values. You shouldn't return a null ResponseHeader in any circumstances.
#Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> login() {
return this::loginUser;
}
public CompletionStage<Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> loginUser(RequestHeader requestHeader, LoginUser user) {
try {
DebugTokenResponse.DebugTokenResponseData response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
ResponseHeader responseHeader;
if (!response.isValid()) {
responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
} else {
responseHeader = ResponseHeader.OK.withStatus(200);
}
return completedFuture(Pair.create(responseHeader, response));
} catch (ExecutionException | InterruptedException | JsonProcessingException e) {
LOG.error(e.getMessage());
throw e;
}
}
Finally, it appears that fbClient.verifyFacebookToken is a blocking method (it doesn't return until the call completes). Blocking should be avoided in a Lagom service call, as it has the potential to cause performance issues and instability. If this is code you control, it should be written to use a non-blocking style (that returns a CompletionStage). If not, you should use CompletableFuture.supplyAsync to wrap the call in a CompletionStage, and execute it in another thread pool.
I found this example on GitHub that you might be able to adapt: https://github.com/dmbuchta/empty-play-authentication/blob/0a01fd1bd2d8ef777c6afe5ba313eccc9eb8b878/app/services/login/impl/FacebookLoginService.java#L59-L74