How do you escape a . (full stop) or / so it doesn't change URL's meaning? - angularjs

I have a Web API 2.0 service which defines a particular route:
/api/someEntityGroup/{entityName}
I'm calling this enpoint using Angular $resource service.
The problem is when user wants to provide an entity name with characters that have a specific meaning in URL:
404 Not found - . (full stop), /, +
400 Bad request - ?, :, &, %, *, <, >
And these are the ones I've encountered. There may be others that may be problematic as well and I'm not even aware of them (yet).
If I use window.escape() function these still don't work, but I mainly get 404 back (the only exception being * which still returns 400 Bad request).
My code
Angular resource creation:
.factory("entityResource", ["$resource", function() {
return $resource("/api/entities/:id", null, {
search: {
method: "GET",
url: "/api/entities/:name",
isArray: true
}
});
}]);
How I call it in my code:
entityResource.search({ query: scope.name }, function(data) {
...
});
My Api controller action:
[RoutePrefix("/api/entities")]
public class EntitiesController: ApiController
{
[Route("{searchQuery}")]
public IEnumerable<Interest> Get(string searchQuery)
{
return this.interestService.Search(searchQuery);
}
...
}

I can shed some light on your 404 Not found issue when using ., /, + characters.
The issue isn't with Angular but rather with Web API and the way it resolves routes. Urls that Web API interprets as being managed resources (e.g. static content, pages etc.) it will try to resolve independently.
Set the following in your web.config to disable this behavior and force WebAPI to run all requests through your modules:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
Just a warning - If your Web API is hosted together with something like MVC or a static website, the above is not recommended as it will force all managed resources (pages, MVC routes, content[css,js,images]) through your API modules and there will be a performance impact. However, if all the API is doing is serving resource routes I would recommend enabling the above.

Related

Angular Put request not working with Laravel 5.3 hosted on azure

I am sending PUT request on my Laravel 5.3 application that is hosted on azure webapps. But I receive a delayed response 504 (Gateway Timeout). While It is working on POSTman (chrome extension).
this is my angular code:
put : function (id, params) {
params.api_token = TOKEN;
return $http.put(url+'/lead/'+id, params);
},
And running this would give me 504 (Gateway Timeout) after 1 min
I have also setup web.config to handle PUT & DELETE. Described here in detail.
<handlers>
<remove name="PHP54_via_FastCGI" />
<add name="PHP54_via_FastCGI" path="*.php" verb="GET, PUT, POST, HEAD, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK" modules="FastCgiModule" scriptProcessor="D:\Program Files (x86)\PHP\v5.4\php-cgi.exe" resourceType="Either" requireAccess="Script" />
</handlers>
So, Because Apache and IIS servers are different. IIS does not handle PUT and DELETE by default. It also handles params for PUT request differently.
Instead of body, you need to send it in a query string like ../resource?param1=value1. AKA x-www-form-urlencoded This article explains it thoroughly
As of angular, this worked for me.
function (id, params) {
params.api_token = TOKEN;
var params = $httpParamSerializerJQLike(params);
return $http.put(url+'/lead/'+id+'?'+params);
}
NOTE: In addition, your web.config does require <handler> tags to be able to handle these requests. which is defined here

Parsing application.yml in angularjs

I have a application.yml file in my application
spring:
profiles:
active: default,dev
app:
properties:
lucene:
indexInfoFile: ${spring.jpa.properties.hibernate.search.default.indexBase}/index.properties
reindex: false
storage:
home: ${user.home}/xxx
basePath: ${app.properties.storage.home}/uploads/
staticFilesPrefix: /files/
appUrl: /app/
spring:
profiles: dev
http:
multipart:
max-file-size: 3MB
max-request-Size: 3MB
Now in my controller, I am trying to get the data from yml file and the code for the same is
$http.get('/resources/application.yml').then(function (response) {
console.log('entire data is ', response.data);
console.log('basePath is ', response.data.basePath);
});
Entire Data is printing perfectly ( the whole yml file is getting printed) but when ever I am trying to print a particular property like basePath, max-file-size etc I am getting "undefined error".
My question is how to get a particular property to be printed on the console.
I would not recommend to access the yml file directly in Angular.
The format is difficult to parse (hence your question) and you sooner or later you may not want to expose all your confguration details.
Instead create a rest controller in spring mapped to something like /config
Let spring inject all the configuration values you need using #Value and return a Map or a simple PoJo with exactly the attributes you need.
Spring will convert this to JSON which you can easily be consumed in Angular.

Not able to retrieve data from mobile data using bluemix

I am trying to retrieve data from mobile data services using bluemix.
The code that I am using is:
jQuery.ajax({
type: 'GET',//POST
url: 'https://mobile.ng.bluemix.net:443/data/rest/v1/apps/c3b20173-b6a4-42d0-b543-93da13c1953e/objects?classname=Report&start=0&num=0',
headers: {"IBM-Application-Secret": "2fc3aa67db1ab71a80f4f38aa1770ff802120848"},
data: data,
dataType: 'json',
crossDomain: true,
success : getData,
error : throwError
})
function getData(data)
{
console.log('GOT REPORT' + JSON.stringify(data));
}
function throwError(data)
{
/*var query = Bluemixdata.Query.ofType("Report");
query.find({Severity: "SEV 4"});
console.log('Pallavi'+ JSON.stringify(query));*/
console.log('GOT REPORTError' + JSON.stringify(data));
}
but on executing the control is moving to throwError method somehow may be its not hitting the URL.
Please suggest if I am doing anything wrong. Want to acknowledge that I am getting data through bluemix console and via postman but not able to retrieve using code.
Even tried to retrieve data using Bluemixdata.Query.ofType("Report"); but getting IBMBluemix not defined error message that should be thrown as <script type="text/javascript" src="js/Bluemix/IBMBluemix.js"></script> is already defined in index.html above all the below given js
LogCat Console Output:
08-13 14:36:21.993: E/AndroidProtocolHandler(21658): Unable to open asset URL: file:///android_asset/www/default/js/Bluemix/cordova_plugins.js
08-13 14:36:22.343: I/chromium(21658): [INFO:CONSOLE(10)] "calling bluemix initialize with values----------------", source: file:///android_asset/www/default/js/Services.js (10)
08-13 14:36:22.423: I/chromium(21658): [INFO:CONSOLE(13)] "---- IBM Bluemix Initializing ------", source: file:///android_asset/www/default/js/Services.js (13)
08-13 14:36:22.433: I/chromium(21658): [INFO:CONSOLE(16)] "HiPal", source: file:///android_asset/www/default/js/Services.js (16)
08-13 14:36:22.433: I/chromium(21658): [INFO:CONSOLE(17)] "GOT A PERSON ----[object Object]", source: file:///android_asset/www/default/js/Services.js (17)
08-13 14:36:22.453: I/chromium(21658): [INFO:CONSOLE(36)] "GOT REPORTError{"statusText":"Not Found","status":404,"response":"","responseType":"","responseXML":null,"responseText":"","upload":{"ontimeout":null,"onprogress":null,"onloadstart":null,"onloadend":null,"onload":null,"onerror":null,"onabort":null},"withCredentials":false,"readyState":4,"timeout":0,"ontimeout":null,"onprogress":null,"onloadstart":null,"onloadend":null,"onload":null,"onerror":null,"onabort":null}", source: file:///android_asset/www/default/js/BarChart.js (36)
Is there a reason you are using jQuery.ajax to formulate your REST request? I suspect there is something wrong with the way you are configuring the request.
If you are going to use our hybrid SDK, you can use the APIs we provide, located here to accomplish the same thing.
I also notice that the Bluemix Cordova plugin is having some problems. Try removing your android platform from your cordova project and re-add it. You can try the same with the plugins.
Also, I recommend checking out the sample bluemixcordovadata for more detailed usages and general help.
Warning: Please avoid pasting your app secret since this is a public forum.

Symfony2: allow all unmatched routes to be accessed anonymously

I have Symfony2 application separated into 2 bundles: BackendBundle for API and FrontendBundle for AngularJS "client". Everything works under firewall.
BackendBundle has entities, handles API routes; FrontendBundle has Angular views, routing etc. and has only one controller with wildcard:
class AngularController extends Controller {
/**
* #Route("/{route}", name="angular_index_all_unmatched_routes", requirements={"route" = ".*"})
* #Template("FrontendBundle::index.html.twig")
*/
public function angularIndexAction($route) {
return ['route' => $route];
}
}
FrontendBundle routing is defined as last resource in app/config/routing.yml, to be invoked only if any other route was not matched. Thanks to that, it can handle Angular HTML5-mode routes if they're accessed directly (for example copy-paste) - and it works ok.
What I want to do, is define firewall and/or access control in way that all those unmatched routes (handled by AngularController::angularIndexAction()) could be accessible by anonymous user.
Why? I want to open some API routes (via frontend proxy) to be accessible by non-users (for example confirmation URLs sent by email, with some message to user).
I don't want to hardcode access control list for every anonymous "Angular" route, I would like to do it only for API routes. At the end, those unmatched routes should open Angular's index which should know if user is logged in (for displaying full or simplified layout) and should handle Angular routes and display some kind of "Access denied" message if request failed (there is Symfony listener and Angular's $provide interceptor for that).
Any suggestions?
Edit: #Security annotation on AngularController::angularIndexAction() does not work, it still redirects to firewall entry point.
Edit2: Here is fragment of security.yml
firewalls:
unsecured:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
anonymous: true
secured:
pattern: '^.*$'
form_login:
login_path: /our-provider/login
check_path: /our-provider/callback/
anonymous: true
entry_point: our_provider.entry_point
access_control:
- { path: '^/our-provider/(login(/[a-zA-Z]+)?|logout|redirect|callback)', roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: '^/', roles: ROLE_USER }
I know that { path: '^/', roles: ROLE_USER } will redirect all routes to login page if user is not logged in. I assumed it's obvious and did not mentioned it. What I want is force ROLE_USER for matched routes and let IS_AUTHENTICATED_ANONYMOUSLY for those unmatched, without explicitely defining each frontend "proxy-route". In my case there is not 404 Symfony page, because everything goes to angular_index_all_unmatched_routes route and there Angular routing definition decides if there is something to handle or not.
I haven't tried this, and I cannot begin to guess your existing security/route setup in security.yml but I guess you could whitelist the method with IS_AUTHENTICATED_ANONYMOUSLY. From the Symfony docs:
All users (even anonymous ones) have this - this is useful when whitelisting URLs to guarantee access - some details are in How Does the Security access_control Work?.
So, for example, if you were using the #Security annotation you could do something like (not tested):
class AngularController extends Controller {
/**
* #Route("/{route}", name="route", requirements={"route" = ".*"})
* #Template("FrontendBundle::index.html.twig")
* #Security("has_role('IS_AUTHENTICATED_ANONYMOUSLY')")
*/
public function angularIndexAction($route) {
return ['route' => $route];
}
}
More on the #Security annotation here.
Hope this helps :)
Edit
All that said, when you define/restrict your routes under access_control in security.yml, the matching process stops on the first match. I assume that you have some role-restricted paths, which you should define explicitly - and put them first, so if they match the process stops.
Otherwise, you should be able to add a catch-all route, enforced by role IS_AUTHENTICATED_ANONYMOUSLY. Since the path definition of a route is a regex, something like ^/ should catch anything that is not explicitly defined. Just make sure and place it after your restricted route definitions.
You would not need for the #Security annotation in this case.
Edit 2
I tried mocking this out using a clean instance and HTTP BasicAuth but what I was trying to achieve was the following, which I understand as similar to your use case:
Create a backend controller with routes / and /api/ and trigger a HTTP BasicAuth authentication popup
Create a frontend controller with route /{route} that would match everything else and authenticate anonymously.
My firewall and access_control configuration looks like this:
security:
encoders:
# encoder config here
providers:
# provider config here
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured:
anonymous: ~
http_basic: ~
access_control:
- { path: ^/$, roles: ROLE_USER }
- { path: ^/api/, roles: ROLE_USER }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Access control paths are regexes, so ^/$ and ^/ are not the same. The former will only match exactly to route /. The latter will match any route that begins with /; e.g: /home, /products, /contact etc.
Indeed, the latter will match and anonymously authenticate /api, but it will not match /api/, or /api/1 etc. as these are explicitly defined and restricted to ROLE_USER.
So the general idea is to explicitly and (if possible) exactly match the routes you want to restrict, and declare those first. The last declaration ^/ should openly catch any other route that falls through.

NetworkError: 405 Method Not Allowed AngularJS REST

In AngularJS, I had the following function, which worked fine:
$http.get( "fruits.json" ).success( $scope.handleLoaded );
Now I would like to change this from a file to a url (that returns json using some sweet Laravel 4):
$http.get( "http://localhost/fruitapp/fruits").success( $scope.handleLoaded );
The error I get is:
"NetworkError: 405 Method Not Allowed - http://localhost/fruitapp/fruits"
What's the problem? Is it because fruit.json was "local" and localhost is not?
From w3:
10.4.6 405 Method Not Allowed
The method specified in the Request-Line is not allowed for the resource
identified by the Request-URI. The response MUST include an Allow header
containing a list of valid methods for the requested resource.
It means the for the URL: http://localhost/fruitapp/fruits The server is responding that the GET method isn't allowed. Is it a POST or PUT?
The angular js version you are using would be <= 1.2.9.
If Yes, try this.
return $http({
url: 'http://localhost/fruitapp/fruits',
method: "GET",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
I had a similar issue with my SpringBoot project, I was getting the same error in the browser console but I saw a different error message when I looked at the back-end log, It was throwing this error: "org.springframework.web.HttpRequestMethodNotSupportedException, message=Request method 'DELETE' not supported " It turned out that I was missing the {id} parameter in the back-end controller:
** Wrong code :**
#RequestMapping(value="books",method=RequestMethod.DELETE)
public Book delete(#PathVariable long id){
Book deletedBook = bookRepository.findOne(id);
bookRepository.delete(id);
return deletedBook;
}
** Correct code :**
#RequestMapping(value="books/{id}",method=RequestMethod.DELETE)
public Book delete(#PathVariable long id){
Book deletedBook = bookRepository.findOne(id);
bookRepository.delete(id);
return deletedBook;
}
For me, it was the server not being configured for CORS.
Here is how I did it on Azure: CORS enabling on Azure
I hope something similar works with your server, too.
I also found a proposal how to configure CORS on the web.config, but no guarantee: configure CORS in the web.config. In general, there is a preflight request to your server, and if you did a cross-origin request (that is from another url than your server has), you need to allow all origins on your server (Access-Control-Allow-Origin *).

Resources