Xamarin PCL that makes requests to REST service and returns models - xamarin.android

So as recommended I'd like to use RestSharp to handle REST web service. I am developing iOS and Android application and I would love to make PCL that makes requests to the service and just returns parsed results (eg. array of User objects).
So how do I get RestSharp in my PCL, tried NuGet, components are not for PCLs and seriously bad would be to just download source files and copy them in the project, I want to keep some dependency management in place.
What is the best practice? Am I looking at this problem at wrong angle?

RestSharp doesn't support PCLs. I'd suggest checking out PortableRest, or just using a combination of HttpClient and Json.NET.

I use dependency injection so I can support non-PCL JSON parsers. I also plan to give the native HttpClient wrappers from the component store a try. By using non-PCL code you will gain quite a lot in performance compared to Json.NET etc.
Link to source code
Text library has serializer interfaces, Web has the IRestClient.
Modern HTTP Client from the component store.

Below modifications worked for me and will be glad if it works out to you.
Try using modernhttpclient in your PCL. And inAndroid project ensure you have the below packages.
Microsoft.Bcl.Build
Microsoft.Bcl
Microsoft.Net.Http
modernhttpclient
Along with that in application manifest under required permissions give permissions to the below.
Access_Network_State
Access_wifi_state
Internet
Ideally when you try to add Microsoft.Bcl into your Android project targettting monoandroid it will throw out error, so try to add the nuget refrence in the above order.

I developed a really simple REST client to perform Http requests easily. You can check it on my Github repo. The API is really simple:
await new Request<T>()
.SetHttpMethod(HttpMethod.[Post|Put|Get|Delete].Method) //Obligatory
.SetEndpoint("http://www.yourserver.com/profilepic/") //Obligatory
.SetJsonPayload(someJsonObject) //Optional if you're using Get or Delete, Obligatory if you're using Put or Post
.OnSuccess((serverResponse) => {
//Optional action triggered when you have a succesful 200 response from the server
//serverResponse is of type T
})
.OnNoInternetConnection(() =>
{
// Optional action triggered when you try to make a request without internet connetion
})
.OnRequestStarted(() =>
{
// Optional action triggered always as soon as we start making the request i.e. very useful when
// We want to start an UI related action such as showing a ProgressBar or a Spinner.
})
.OnRequestCompleted(() =>
{
// Optional action triggered always when a request finishes, no matter if it finished successufully or
// It failed. It's useful for when you need to finish some UI related action such as hiding a ProgressBar or
// a Spinner.
})
.OnError((exception) =>
{
// Optional action triggered always when something went wrong it can be caused by a server-side error, for
// example a internal server error or for something in the callbacks, for example a NullPointerException.
})
.OnHttpError((httpErrorStatus) =>
{
// Optional action triggered when something when sending a request, for example, the server returned a internal
// server error, a bad request error, an unauthorize error, etc. The httpErrorStatus variable is the error code.
})
.OnBadRequest(() =>
{
// Optional action triggered when the server returned a bad request error.
})
.OnUnauthorize(() =>
{
// Optional action triggered when the server returned an unauthorize error.
})
.OnInternalServerError(() =>
{
// Optional action triggered when the server returned an internal server error.
})
//AND THERE'S A LOT MORE OF CALLBACKS THAT YOU CAN HOOK OF, CHECK THE REQUEST CLASS TO MORE INFO.
.Start();

Related

NestJS microservices error with "No matching message handler"

I'm building an application with microservices communicating through RabbitMQ (request-response pattern). Everything works fine but still I have a problem with error "There is no matching message handler defined in the remote service." - When I send POST to my Client app, it should simply send the message with data through client (ClientProxy) and the Consumer app should response. This functionality actually works, but always only for the second time. I know it sounds strange but on my first POST request there is always the error from Client and my every second POST request works. However this problem is everywhere in my whole application, so the particular POST request is just for the example.
Here is the code:
Client:
#Post('devices')
async pushDevices(
#Body(new ParseArrayPipe({ items: DeviceDto }))
devices: DeviceDto[]
) {
this.logger.log('Devices received');
return this.client.send(NEW_DEVICES_RECEIVED, devices)
}
Consumer:
#MessagePattern(NEW_DEVICES_RECEIVED)
async pushDevices(#Payload() devices: any, #Ctx() context: RmqContext) {
console.log('RECEIVED DEVICES');
console.log(devices);
const channel = context.getChannelRef();
const originalMsg = context.getMessage();
channel.ack(originalMsg);
return 'ANSWER';
}
Client has the RMQ settings with queueOptions: {durable: true} and the consumer as well queueOptions: {durable: true} with noAck: false
Please do you have any ideas what may causes the problem? I have tried sending the data with JSON.stringify and changing the message structure to {data: devices} but the error is still there.
I had same error and finally solve it today.
In my project, there is an api-gateway as a hybrid application to receive requests and pass data to other systems, every second request gives an error like below.
error: There is no matching message handler defined in the remote service.
Then I tried to remove the api-gateway hybrid application scope in the code below, the error is gone, hope this helps you out with this.
// api-gateway main.ts
const app = await NestFactory.create(AppModule);
// run as a hybrid app —→ remove it
app.connectMicroservice({
transport: Transport.RMQ,
noACK: false,
options: {
urls: [`amqp://${rmqUser}:${rmqPassword}#127.0.0.1:5672`],
queue: 'main_queue',
queueOptions: {
durable: false,
},
},
});
// run hybrid app
await app.startAllMicroservices(); —→ remove it
await app.listen(3000);
I solved this issue by placing the #EventPattern decorator on to a #Controller decorator method
I had this error while NOT using RabbitMQ. I found very little help online around this error message outside of it being related to RabbitMQ.
For me it was an issue where I was importing a DTO from another microservice in my microservice's Controller. I had a new DTO in my microservice that has a similar name to one in another microservice. I accidentally selected the wrong one from the automated list.
Since there wasn't any real indicator that my build was bad, just this error, I wanted to share in case others made the same mistake I did.
I encountered this same issue today and could not find any solution online and stumbled upon your question. I solved it in a hacky way and am not sure how it will behave when the application scales.
I basically added one #EventPattern (#MessagePattern in your case) in the controller of the producer microservice itself. And I called the client.emit() function twice.
So essentially the first time it gets consumed by the function that is in the producer itself and the second emit actually goes to the actual consumer.
This way only one POST call is sufficient.
Producer Controller:
#EventPattern('video-uploaded')
async test() {
return 1;
}
Producer client :
async publishEvent(data: VideosDto) {
this.client.emit('video-uploaded', data);
this.client.emit('video-uploaded', data);
}
I've experienced the same error in my another project and after some research I've found out that problem is in the way of distributing messages in RabbitMQ - named round-robin. In my first project I've solved the issue by creating a second queue, in my second project I'm using the package #golevelup/nestjs-rabbitmq instead of default NestJS library, as it is much more configurable. I recommend reading this question

$batch request resulting in error "Default changeset implementation allows only one operation"

I am making a worklist application using SAPUI5. The problem is that when I create an entry and then create another one right after that, I get the following error:
Default changeset implementation allows only one operation.
I checked the $batch header and I see that there is a MERGE and a POST, with the MERGE updating the previous entry for some reason. Can anyone shed some light? Could it be a backend error and not a UI5 error?
Creating the new entry:
_onMetadataLoaded: function() {
var oModel = this.getView().getModel();
var that = this;
// ...
oModel.read("/USERS_SET", {
success: function(oData) {
var oProperties = {
Qmnum: "0",
Otherstuff: "cool"
};
that._oContext = that._oView.getModel().createEntry("/ENTITYSET", {
properties: oProperties
});
that.getView().setBindingContext(that._oContext);
// ...
}
});
},
handleSavePress: function(oEvent) {
// ...
this.getView().getModel().submitChanges({
success: function(oData) {
// ...
},
error: function(oError) {
// ...
}
});
},
tl-dr: Apparently you must be using the SAP Gateway. If you do not need to process those requests in one transaction then send them in different changesets. If you do not need batch calls at all consider turning it off by supplying your model with "useBatch": false upon instantiation. However if you need to process the requests together in one transaction then you have to read the details below.
In order to understand the problem you have to understand how the gateway and the batch and changeset requests work.
Batch requests consist of multiple requests bundled together. The purpose is to open only one connection and group together relevant requests so that the overhead is minimalized. Changesets form smaller blocks inside batch requests, where modification requests can be bundled and processed together in order to ensure an all-or-nothing characteristic.
So on the gateway side: there are two relevant classes for your OData service, assuming that you have used the SAP Gateway Service Builder (SEGW transaction). There is one with the ending ...DPC and one with ...DPC_EXT. Don't touch the former, it will be always regenerated when you update your service in the service builder. The latter is the one that we will need in this example. You will have to redefine at least two methods:
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS
By default the changeset_begin method will only allow changeset processing for changesets where the number of requests equals to one. This can be handled automatically that's why a limitation exists. If there were more requests one could not ensure their processing automatically as they could have a business dependency on each other.
So make sure to allow a bundled (deferred mode) processing of changesets under the desired conditions:
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN: first call the super->/iwbep/if_mgw_appl_srv_runtime~changeset_begin method in a try catch block, then loop at it_operation_info to decide and narrow down processing only in selected cases and then allow cv_defer_mode only for the selected cases, otherwise throw a /iwbep/cx_mgw_tech_exception=>changeset_not_supported exception.
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS: all requests will be available in the it_changeset_request. Make sure to fill the ct_changeset_response table with the responses.
METHOD /iwbep/if_mgw_appl_srv_runtime~changeset_process.
DATA:
lv_operation_counter TYPE i VALUE 0,
lr_context TYPE REF TO /iwbep/cl_mgw_request,
lr_entry_provider TYPE REF TO /iwbep/if_mgw_entry_provider,
lr_message_container TYPE REF TO /iwbep/if_message_container,
lr_entity_data TYPE REF TO data,
ls_context_details TYPE /iwbep/if_mgw_core_srv_runtime=>ty_s_mgw_request_context,
ls_changeset_response LIKE LINE OF ct_changeset_response.
FIELD-SYMBOLS:
<fs_ls_changeset_request> LIKE LINE OF it_changeset_request.
LOOP AT it_changeset_request ASSIGNING <fs_ls_changeset_request>.
lr_context ?= <fs_ls_changeset_request>-request_context.
lr_entry_provider = <fs_ls_changeset_request>-entry_provider.
lr_message_container = <fs_ls_changeset_request>-msg_container.
ls_context_details = lr_context->get_request_details( ).
CASE ls_context_details-target_entity.
WHEN 'SomeEntity'.
"Do the processing here
WHEN OTHERS.
ENDCASE.
ENDLOOP.
ENDMETHOD.
From the error I can tell you must be using SAP GW :-) This happens only for batch requests containing more than one create/delete/update calls and it's related to transaction security ("all or nothing"). What you have to do is redefining the corresponding GW method, I think it was CHANGESET_BEGIN. See https://archive.sap.com/discussions/thread/3562720 for some details (can't offer more for now...).

How to purposely delay an AJAX response while testing with Capybara?

I have a React component that mimics the "link preview" feature that most modern social media sites have. You type in a link and it fetches the image, title, etc...
I do this by having the React component make an AJAX call back to my server to fetch the URL preview data.
While it's fetching I show an intermediate "loading" state (i.e. some loading icon or spinning wheel)
The relevant React snippet looks like
this.setState({ isLoadingAttachment: true })
return $.ajax({
type: "GET",
url: some_url,
dataType: "json",
contentType: "application/json",
}).success(function(response){
// Succesful! Do Success stuff
component.setState({ isLoadingAttachment: false })
}).error(function(response) {
// Uh oh! Handle failure stuff
component.setState({ isLoadingAttachment: false })
});
Note how the isLoadingAttachment state variable is only valid for a brief second while the server is doing the fetching. Both the success and error scenarios immediately disable it.
I'd like to test some functionality during my "loading" state with my Capybara feature specs. I've mocked all the web calls and the data to be returned by the server, but it all happens so quickly that it passes through the "loading" state before I can even run any expect().. statement on it. I also purposely don't call wait_for_ajax so the page will go ahead without waiting for the ajax, but it's still too fast.
Lastly I also tried purposefully delaying the server call by 1.0 second, but that didn't work either. I assume because the whole thing is single threaded somehow?
# `foo` is an arbitrary method called during the server-side execution
allow_any_instance_of(MyController).
to receive(:foo) { sleep(1.0) }.and_call_original
Any thoughts on how I could do this?
Thanks!
Capybara starts up the app server in a different thread than the tests, however if you're using the default Capybara.server setting you may have issues with your app calling back to itself since it uses webrick by default. Instead you should specify Capybara.server = :puma. Beyond that, mocking responses is generally a bad idea in feature specs (which are generally meant to be end-to-end tests) since it means you're not actually testing your apps code the way it would run in production anymore. A better solution is to use something like puffing-billy - https://github.com/oesmith/puffing-billy - to mock web responses outside of your apps code which would allow you to do something like
proxy.stub('https://example.com/proc/').and_return(Proc.new { |params, headers, body|
sleep 2
{ :text => "Your results"}
})

IE 11 + SignalR not working

Strange behavior is happening when using signalR with IE 11. Scenario:
We have some dispatcher type functionality where the dispatcher does some actions, and the other user can see updates live (querying). The parameters that are sent come through fine and cause updates on the IE client side without having to open the developer console.
BUT the one method that does not work (performUpdate - to get the query results - this is a server > client call, not client > server > client) - never gets called. IT ONLY GETS CALLED WHEN THE DEVELOPER CONSOLE IS OPEN.
Here's what I've tried:
Why JavaScript only works after opening developer tools in IE once?
SignalR : Under IE9, messages can't be received by client until I hit F12 !!!!
SignalR client doesn't work inside AngularJs controller
Some code snippets
Dispatcher side
On dropdown change, we get the currently selected values and send updates across the wire. (This works fine).
$('#Selector').on('change', function(){
var variable = $('#SomeField').val();
...
liveBatchHub.server.updateParameters(variable, ....);
});
Server Side
When the dispatcher searches, we have some server side code that sends out notifications that a search has been ran, and to tell the client to pull results.
public void Update(string userId, Guid bId)
{
var context = GlobalHost.ConnectionManager.GetHubContext<LiveBatchViewHub>();
context.Clients.User(userId).performUpdate(bId);
}
Client side (viewer of live updates)
This never gets called unless developer tools is open
liveBatchHub.client.performUpdate = function (id) {
//perform update here
update(id);
};
Edit
A little more information which might be useful (I am not sure why it makes a difference) but this ONLY seems to happen when I am doing server > client calls. When the dispatcher is changing the search parameters, the update is client > server > client or dispatcher-client > server > viewer-client, which seems to work. After they click search, a service in the search pipeline calls the performUpdate server side (server > viewer-client). Not sure if this matters?
Edit 2 & Final Solution
Eyes bloodshot, I realize I left out one key part to this question: we are using angular as well on this page. Guess I've been staring at it too long and left this out - sorry. I awarded JDupont the answer because he was on the right track: caching. But not jQuery's ajax caching, angulars $http.
Just so no one else has to spend days / nights banging their heads against the desk, the final solution was to disable caching on ajax calls using angulars $http.
Taken from here:
myModule.config(['$httpProvider', function($httpProvider) {
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
// Answer edited to include suggestions from comments
// because previous version of code introduced browser-related errors
//disable IE ajax request caching
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
// extra
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
}]);
I have experienced similar behavior in IE in the past. I may know of a solution to your problem.
IE caches some ajax requests by default. You may want to try turning this off globally. Check this out: How to prevent IE from caching Ajax with jQuery
Basically you would globally switch this off like this:
$.ajaxSetup({ cache: false });
or for a specific ajax request like this:
$.ajax({
cache: false,
//other options...
});
I had a similar issue with my GET requests caching. My update function would only fire off once unless dev tools was open. When it was open, no caching would occur.
If your code works properly with other browsers, So the problem can be from the SignalR's used transport method. They can be WebSocket, Server Sent Events, Forever Frame and Long Polling based on browser support.
The Forever Frame is for Internet Explorer only. You can see the Introduction to SignalR to know which transport method will be used in various cases (Note that you can't use any of them on each browser, for example, IE doesn't support Server Sent Events).
You can understand the transport method being used Inside a Hub just by looking at the request's QueryString which can be useful for logging:
Context.QueryString["transport"];
I think the issue comes from using Forever Frame by IE likely, since sometimes it causes SignalR to crash on Ajax calls. You can try to remove Forever Frame support in SignalR and force to use the remaining supported methods by the browser with the following code in client side:
$.connection.hub.start({ transport: ['webSockets', 'serverSentEvents', 'longPolling'] });
I showed some realities about SignalR and gave you some logging/trace tools to solve your problem. For more help, put additional details :)
Update:
Since your problem seems to be very strange and I've not enough vision around your code, So I propose you some instructions based on my experience wish to be useful:
Setup Browser Link in IDE suitable
checkout the Network tab request/response data during its process
Make sure you haven't used reserved names in your server/client side
(perhaps by renaming methods and variables)
Also I think that you need to use liveBatchHub.server.update(variable, ....); instead of liveBatchHub.server.updateParameters(variable, ....); in Dispatcher side to make server call since you should use server method name after server.

Sencha Touch JSONP error

EDIT:
I came across a confirmation of what I suspected: Using the twitter search API with JSONP causes the problem in isolation, so it seems that something is going wrong with Twitter.
See:
http://search.twitter.com/search.json?q=%23jimromeisburning&callback=dog
About 3/5 times, as of 3:44PM CT on 14 June, Twitter returns garbage. The rest of the time, it returns a valid javascript function call.
I'm using Sencha Touch to make a JSONP request to the Twitter search API, and about 1/100 times, I'm getting a javascript error that kills further polling:
Uncaught SyntaxError: Unexpected token ILLEGAL
So far I've tried the following with no leads:
Wrapping the call to Ext.util.JSONP.request({}) in a try/catch block. Doesn't catch the error (presumably because it's being called from a script tag in an iframe)
Dumping the query parameter passed into JSONP.request to make sure that it's valid. It is.
Looking for a pattern-- it seems to happen at unexpected times. Could be the very first request, or it could be 100 requests down the line.
My best guess is that Twitter is sending back garbage some of the time. That's ok, I just need a way to handle that error. Unfortunately, as far as I can tell, Sencha Touch doesn't have any built-in error handling for its JSONP requests.
Have you seen anything like this before? Do you have any ideas?
Thanks!
Here's what the ornery JSONP script response looks like:
Ext.util.JSONP.callback(�Řo�6ǿ
�`)֥��k�em��+�`�
-�-��RT��w�ɖ���$v�-A^ґ���Ow�|�4Tua*+����ת����Ⱥ��VbšҐ�֡5Ҫ/
芒�[�o�ƌ��NnjE9褪���*��N3�1j;QRǏ®T��E�r4��S
�#��w|��!a.���ġ�%�����#��*����>Z8^_h��녾z>u]�E��ϸ�V��u�k&##k
)Hc}=���;o%�.
�׍���L��5�T�B*�?������{���꒼z�M���.}/dm�=���곒5i�KA��y����Q�n���n����
Һ�x��̼R�N���q�k��<�\+s�*���&[��DCњH�WE�Ƴ���uhj�ڼ����ȋ��,t"�>�'����o�VnK��ⳍ�\�p,'9�
��:~{��"���8n�
�x�ͫK���C�mx(�<�
����3>������B]A_�L�+=�%fY�*1��/���wO�vc�Z8d=)̦1����߳35����-F����.f���D|�.z6����Xs��s\愶 ���M*Z�D�� �7ڈ�)ϗ cA�^9N�n�aN#�w�/^
P��¸-�E�$R�����<�K�n�3A3��򳀇�L+�mI��vՃ�0Ǎ}o���Q��4�����=e��n�q8��ģ�����.�C)s=�:+>�O�h9�C2Q5Y���PA����*�3y1�t�`���g��WǠ�YB�O�/�{+.�[����,ߴ��/�yQ�<t(���|ߥ�G����ݾ�b��ijBS�9��.E�>�D%�I���jz�켻0�q��0`Q��.��.�>88�춖��(i4fȻgW#�aI*�������#���z�j�\5g��\�n���e���c��7�o��w�z�,�|/��+�N�����}�z+v����nd�
NY�R��o�� }��hĚ�;��g�D2��9�����:-e�����^#Ua���j2C��#�U���k�9���I�'�ܐ���/H3�q(��d�<�$����q~͛5��}��V�ft�'U'{���0�����Ø��sC?|B��0I���B�E] %�c��S���6LC�x�Y�EQT�*�Akr��÷OyOn��N�7iSkW` �F�q�!�����+,[���I��1
�i�3C*����_��h�K �� ^�{�V|YìM�7ŬW�t��'��ek��y�lr�l�WM)Đ�>�O���F,`�w��r��6�a�X����B�n�2t�O\�R7��O�n���!`�#
M� i���MU]5_�k�TMR�� 'Z��Y��C�Sn�q.�V��";d�`x��k Β��Mr��/�����٬A��Fq�B|L���>+,B0��R��K�����˵u�_~縫}��Zw����E���7�K����:.�i�n%��4l�/F���������5_�����);
I recently answered a similar question in which the OP was encountering fail whales while using the search API.
I found this question which had some interesting answers regarding error handling in JSONP. To summarize, one approach is to wrap all errors returned by the server in JSON, and another provides a link to jQuery-JSONP, a nice looking reinterpretation of jQuery's JSONP implementation.
Interesting. You would need to override the callback method in the Ext.util.JSONP class, and wrap the line which calls the callback, in a try/catch block. Then in the catch block try and call an errorCallback (which you need to define in your actual JSONP request).
Ext.util.JSONP.callback = function(json) {
try {
this.current.callback.call(this.current.scope, json);
} catch(e) {
this.current.errorCallback.call(this.current.scope);
}
document.getElementsByTagName('head')[0].removeChild(this.current.script);
this.next();
};

Resources