AngularJS DELETE with Tomcat causes 405 Request method 'DELETE' not supported - rest

I'm using Spring Data Neo4j Rest backend served on Tomcat (8.0.27) with Angular frontend, all served locally at http://localhost:8080/. When I try to delete a record with either $http.delete() or with $resource().delete(), tomcat returns a 405 not supported error.
However, using $http({method: 'DELETE', url: '/competitors/13'}) works fine. Unfortunately, this does not allow me to use the angular $resource service instead of $http, which is my goal. (As an aside, PUT creates the same 405 response as DELETE)
I am also able to use DELETE from command line. This works fine:
curl -X DELETE http://localhost:8080/competitors/13
I have disable csrf in WebSecurityConfig in case that has anything to do with it.
I have also added readonly init-param to web.xml to try to force tomcat to accept DELETE, like so:
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Proto v2 Application</display-name>
<servlet>
<servlet-name>proto</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>proto</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Since I've verified that the server is able to perform the delete, I will omit all Java code. Here is my complete .js file with the angular module:
var magneticApp = angular.module('magneticApp', ['ngResource'])
.controller('defaultCtrl', function ($scope, $http, $resource, baseUrl) {
$scope.Competitor = $resource("/competitors/:id", {id: "#id"});
$scope.listItems = function () {
var query = $scope.Competitor.get(function () {
console.log('inside query');
});
query.$promise.then(
function successCallback(data) {
$scope.competitors = data._embedded.competitors;
},
function errorCallback(data) {
console.log('error:');
console.dir(data);
}
);
};
/*
* THIS DELETE FUNCTION RETURNS A 405 NOT SUPPORTED ERROR
*/
$scope.deleteItem = function (item) {
var query = $scope.Competitor.delete({}, item.itemId);
query.$promise.then(
function successCallback(data) {
console.log('success:');
$scope.competitors.splice($scope.competitors.indexOf(item), 1);
},
function errorCallback(data) {
console.log('error:');
console.dir(data);
}
);
};
/*
* THIS DELETE FUNCTION WORKS PROPERLY
*/
$scope.deleteItemWorksCorrectly = function (item) {
$http({method: 'DELETE', url: '/competitors/' + item.itemId})
.then(
function successCallback(data) {
$scope.competitors.splice($scope.competitors.indexOf(item), 1);
},
function errorCallback(data) {
console.log('delete error');
console.dir(data);
}
);
};
$scope.adjustActive = function (item) {
item.id = item.itemId;
console.log('adjustActive');
console.dir(item);
$http.post('/competitors', item)
.success(function (data) {
$http.get('/competitors').success(function (data) {
console.dir(data._embedded.competitors);
$scope.competitors = data._embedded.competitors;
});
});
};
$scope.addItem = function (itemName) {
var item = {name: itemName};
$http.post('/competitors', item)
.success(function (data) {
$http.get('/competitors').success(function (data) {
console.dir(data._embedded.competitors);
$scope.competitors = data._embedded.competitors;
//magneticApp.controller('defaultCtrl').$scope.competitors = data._embedded.competitors;
});
});
};
$scope.listItems();
});
Can anyone shed some light on this? Thanks.

Related

How to Resolve Access Control Allow Origin Error

I am trying to retrieve data from 3rd party xml api through jquery ajax call. But it is showing Access Control Allow Origin CORS error. After some twiking I a getting ERR_CONNECTION_RESET error on Chrome.
This is the the Api link from where I am trying to retrieve data
http://202.61.117.90/GRIPS/ChallanDetails/query.do?GRN_NO=192021225551499528
This is my own server url to access the api
http://103.240.91.230:8080/CRM/checkApi.do?method=checkApi
Below is the html/jquery code i am using
<script>
$(document).ready(function(){
$.ajax({
type: "GET" ,
url: "http://202.61.117.90/GRIPS/ChallanDetails/query.do?GRN_NO=192021225551499528" ,
headers: { 'Access-Control-Allow-Origin': 'http://202.61.117.90/*' },
dataType: "xml" ,
success: function(xml) {
$(xml).find('GRIPS_EPAYMENT').each(function(){
var grnNo = $(this).find('GRN_NO').text();
var grnDate = $(this).find('GRN_DATE').text();
var brnNo = $(this).find('BRN_NO').text();
$("#grndate").append(grnDate);
$("#brnno").append(brnNo);
});
}
});
});
</script>
I am using tomcat 8.5 and added following filter in web.xml
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Note: when I am calling same api from my local server - it works fine

Cannot read property 'Authorization' of undefined with Nuxt Auth & Axios

I have been using nuxt/auth-next and axios modules with nuxt project since last 3-4 months, everything was working fine since yesterday but now whenever I try to send axios request to public APIs without passing Authorization in headers, I get this error
Cannot read property 'Authorization' of undefined with Nuxt Auth & Axios
Attached is a screenshot of the page
below is my code in index.js store file
export const actions = {
async nuxtServerInit({ commit }, context) {
// Public profile
if (context.route.params && context.route.params.subdomain) {
context.$axios.onRequest((config) => {
config.progress = false
})
let { data } = await context.$axios.get(
`users/get_user_data_using_subdomain/${context.route.params.subdomain}`,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
)
await context.store.dispatch('artists/setPublicProfile', data.user_data)
}
},
}
This happend to me to when I was using context.app.$axios instead of context.$axios within a injection
Nuxt server is looking for config.headers.common.Authorization.
The example below is a quick win for you:
let { data } = await context.$axios.get(
`users/get_user_data_using_subdomain/${context.route.params.subdomain}`,
{
headers: {
common: null, // or something like this: context.$axios.defaults.headers?.common
'Content-Type': 'multipart/form-data',
},
}
)

Http Response headers missing in chrome, but with Postman they show up

When calling my REST Service in Angular, there are no response headers.
Login method in Angular
login(username: string, password: string) {
const credentials = { "username": username, "password": password };
return this.http.post(this.url, credentials)
.subscribe(
data => console.log(data), // JSON.stringify(data.headers) also is empty
error => console.log(error)
);
}
Output in the Chrome dev tools console
Response {_body: "", status: 200, ok: true, statusText: "OK", headers:
Headers…}headers: Headers_headers: Map(0)_normalizedNames:
Map(0)proto: Objectok: truestatus: 200statusText: "OK"type: 2url:
"http://localhost:8080/backend/rest/login"_body: ""proto: Body
But when I send the same post request with postman, I get the expected result:
Access-Control-Allow-Credentials →true
Access-Control-Allow-Origin →chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Authorization →Bearer eyJ[...]
Connection →keep-alive
Content-Length →0
Date →Mon, 12 Jun 2017 13:19:54 GMT
Server →WildFly/10
Vary →Origin
X-Powered-By →Undertow/1
The REST Service
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(CredentialsDTO credentialsDTO) {
try {
authService.login(credentialsDTO.getUsername(), credentialsDTO.getPassword());
} catch (WrongCredentialsException e) {
return Response.status(Status.FORBIDDEN).entity("WrongCredentialsException").build();
}
// Issue token
String token = issueToken(credentialsDTO.getUsername());
// Return the token on the response
return Response.ok().header(AUTHORIZATION, "Bearer " + token).build();
}
Why can't I see the headers in chrome ?
UPDATE
I am also using a CORSFilter that allowed Javascript to contact my backend in the first place. This is how it is configured in my web.xml
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowSubdomains</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.maxAge</param-name>
<param-value>3600</param-value>
</init-param>
</filter>
<filter-mapping>
<!-- CORS Filter mapping -->
<filter-name>CORS</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
Although I thought it was configured to allow everything, I am not sure if this is related to my problem.
By default CORS responses only exposes these 6 headers to scripts:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
To allow scripts to access other headers sent by the sever, the server needs to send the Access-Control-Expose-Headers Header.
The Access-Control-Expose-Headers response header indicates which headers can be exposed to scripts as part of the response by listing their names.
eg: Access-Control-Expose-Headers: Authorization, X-Foobar
You can tweak your web.xml file including this to allow the Authorization header to be accessed from the script that made the XHR:
<init-param>
<param-name>cors.exposedHeaders</param-name>
<par‌​am-value>Authorizati‌​on</param-value>
<ini‌​t-param>
I used the following solution to allow my custom header in Owin self-hosted web api
using Owin;
using Microsoft.Owin.Cors;
using System.Threading.Tasks;
using System.Web.Cors;
namespace App.Web.App_Start
{
public static class CorsConfig
{
public static void Configure(IAppBuilder app)
{
var allowedOrigins = "comma,separated,list,of,origins";
var corsPolicy = new CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = false,
SupportsCredentials = true
};
corsPolicy.ExposedHeaders.Add("Custom-Header");
foreach (string origin in allowedOrigins.Split(','))
corsPolicy.Origins.Add(origin);
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(corsPolicy)
}
});
}
}
}
I call the method like this CorsConfig.Configure(app); // app is Owin.IAppBuilder
The headers in an HttpResponse object are lazy-loaded, so headers will appear to be empty until you force the values to be loaded. Try calling response.headers.keys() to see all available header names. By the way, this also forces all values to be loaded into the map response.headers.headers.
Be sure to use headers.get('my-header-name') if you only need to access one particular header value.

Getting 404 Not Found - while accessing valid REST service with Jersey + ExtJS

I am working on ExtJS and need to access the REST services. I tried doing that with Spring's MVC REST supporting functionality and it worked well.
Now its kind of mandatory that I have to go with Jersey(JAX-RS). When tried with Jersey, I keep getting 404 error. I think the annotations, URL mapping, etc are fine (since the similar ones worked for Spring)
Below are the relevant code snippets.
Web.xml
<servlet>
<servlet-name>spring-jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.myProducts.controller</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/jersey/*</url-pattern>
</servlet-mapping>
App.js
Ext.onReady(function() {
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
autoSync: true,
model: 'Product',
proxy: {
type: 'rest',
url: 'products',
format: 'json',
headers: {
'Content-Type': 'application/json'
},
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json'
},
api: {
create: 'products/createJ/',
read: 'products/readJ/',
update: 'products/editJ/',
destroy: 'products/deleteJ/'
}
}
});
Controller :
#Component
#Path("products/jersey/products")
public class JerseyProductsController {
#Autowired
private ProductService productService;
#POST
#Path("createJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product createJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.create(myProduct);
}
#PUT
#Path("editJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product editJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.update(myProduct);
}
#DELETE
#Path("deleteJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product deleteJ(#PathParam("id") int id) {
return productService.delete(id);
}
#GET
#Path("readJ/")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public List<Product> allProductsJ() {
return productService.getAll();
}
}
I get 404 for below URL:
Request URL:http://localhost:4080/MyProducts/products/jersey/products/readJ/.json?_dc=1407930853131&page=1&start=0&limit=25
Request Method:GET
Status Code:404 Not Found
Kindly let me know what is being missed.
You may need to use /jersey/products/jersey in your URL since you are having <url-pattern>/jersey/*</url-pattern>
Not sure what's the issue but you could try below one :
<url-pattern>*.do</url-pattern>
I believe its because it could capture requests ending with .do and it reduced the headache of having some pattern in between the URL.
I think your servlet mapping is incorrect. Your Url-Pattern is /jersey/* but in your url is /MyProducts/*
put this in your web.xml
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/MyProducts/*</url-pattern>

How to serve 404's using AngularJS and a RESTful API

Let's say you have an AngularJS application hooked up to a RESTful API and you have a route for "/item/:itemId".
.when('/item/:itemId', {
templateUrl: '/static/partials/item-detail.html',
controller: ItemDetailController
})
angular.module('angServices', ['ngResource']).factory('Item', function($resource) {
return $resource('/api/item/:itemId', {}, {
query: { method: 'GET', params: { itemId: '' }, isArray: true }
});
});
If the user goes to "/item/9" and an object with the itemId 9 does not exist, Angular will receive a 404 from the API, but will not naturally return a 404 to the user.
In other questions, I've seen people suggest creating an interceptor and having Angular redirect to a 404 error page when a resource is not found.
var interceptor = ['$rootScope', '$q', function(scope, $q) {
...
function error(response) {
if (response.status == 404) { window.location = '/404'; }
...
$httpProvider.responseInterceptors.push(interceptor);
However, I want to return a correct 404 with the original requested URL for SEO purposes.
Also, the solution above first loads the page and then redirects (just like Twitter used to do), so its sub-optimal.
Should I check server-side to first see if the resource exists before passing the request on to the Angular app? The downside of this is that it wouldn't work for broken links within the application.
What is the best way to approach this?
Maybe this jsfiddle can help you.
http://jsfiddle.net/roadprophet/VwS2t/
angular.module('dgService', ['ngResource']).factory("DriveGroup", function ($resource) {
return $resource(
'/', {}, {
update: {
method: 'PUT'
},
fetch: {
method: 'GET',
// This is what I tried.
interceptor: {
response: function (data) {
console.log('response in interceptor', data);
},
responseError: function (data) {
console.log('error in interceptor', data);
}
},
isArray: false
}
}
);
});
var app = angular.module('myApp', ['ngResource', 'dgService']);
app.controller('MainController', ['$scope', 'DriveGroup', function ($scope, svc) {
$scope.title = 'Interceptors Test';
svc.fetch(function (data) {
console.log('SUCCESS');
}, function () {
console.log('FAILURE');
});
}]);
I tried with this and works fine. I only change the fetch method to get.
In your case, you will need to change the console.log('FALIURE'); to $location.path('/404');.
GL!