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

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>

Related

Grails 2.4.5 Swaggydoc plugin: How to prevent the Grails Swaggydoc plugin from overriding my url names or force to use default method names?

I have a grails 2.4.5 application with the grails plugin swaggydoc.
I'm able to get the UI with the documentation, however, the only way I'm able to have the POST, PUT and DELETE requests documentation automatically generated properly with the right url and Http method, is by overriding the RestFulController methods (save, update, and delete).
If I don't do that, the method is considered a get request, even despite having the proper annotation.
However, by doing that, the url names I put in UrlMappings.groovy gets overridden (except the GET requests) in the documentation and follows the default grails url naming behavior of controller/methodName, hence providing wrong urls for the documentation, so the documentation almost becomes useless, as not totally accurate.
pom.xml
...
<!-- API Documentation -->
<!-- Swagger-->
<dependency>
<groupId>com.github.rahulsom</groupId>
<artifactId>swaggydoc-commons</artifactId>
<version>0.28.0</version>
</dependency>
<dependency>
<groupId>org.grails.plugins</groupId>
<artifactId>swaggydoc</artifactId>
<version>0.9</version>
<type>zip</type>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer-v2</artifactId>
<version>2.1.2</version>
</dependency>
...
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
//api documentation
"/api/v1/docs"(controller: 'api')
//Events resource
group "/api/v1/events", {
"/$eventId?"(controller: 'event', action: 'getEvents', method: 'GET', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'update', method: 'PUT', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'save', method: 'POST', parseRequest: true)
}
}
Sample controller
#Secured(['IS_AUTHENTICATED_FULLY'])
#Api(
value = 'Events',
description = "Event APIs",
position = 1,
produces = 'application/json',
basePath = 'api/v1',
protocols = 'http, https'
)
class EventController extends RestfulController {
static responseFormats = ['json']
static scope = "singleton"
#Override
#ApiOperation(
value = "Update event",
httpMethod = 'PUT',
authorizations = #Authorization(value = 'oauth2')
)
#ApiResponses([
#ApiResponse(code = 204, message = 'No Content')
])
#ApiImplicitParams([
#ApiImplicitParam(name = 'Id', value = 'event id', paramType = 'path', dataType ='long')
])
def update() {
...
}
}
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
//api documentation
"/api/v1/docs"(controller: 'api')
//Events resource
group "/api/v1/events", {
"/$eventId?"(controller: 'event', action: 'getEvents', method: 'GET', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'update', method: 'PUT', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'save', method: 'POST', parseRequest: true)
}
}
I was expecting all the apis, as specified by the UrlMappings.groovy to not include any of the methods, however, Swaggydoc isn't even following my custom mapping.
I was expecting at least:
PUT /events/{eventId}
Is there a way to override this behavior, so I don't have to override the RestFulController Http methods (save, update and delete) and choose whatever method name I want and have the documentation urls being accurate?
At this point, I don't mind trying some other reliable plugin too, if that's not possible with the SwaggyDoc plugin.
The ideal scenario is to have the apis documentation automatically generated and have a ui that users can interact with and even pass a token to test the apis and get a response back.
Any feedback on how to do that with Grails 2.4.5 will be greatly appreciated.

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

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.

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

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.

In Extjs5, erase() can't relate to destory api when the proxy type is rest

This is my viewController:
onRestDeleteClick: function(){
var ouType = Ext.create('MyApp.model.OuType',
{
id: 49,
ouTypeName: 'Lenlee',
entityName: 'Lenlee'
});
ouType.erase();
}
The model whose id is 49 exists in database.
This is the OuType model:
Ext.define('MyApp.model.OuType', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.Field'
],
fields: [
{
name:'id'
},
{
name: 'ouTypeName'
},
{
name: 'entityName'
}
],
proxy:{
type: 'rest',
api: {
read: 'role/read',
update: 'role/update',
create: 'role/create',
destory: 'role/destory'
}
}
});
This is my server class:
#RequestMapping("/role")
#Controller("sysRoleContro")
public class SysRoleController {
…………
…………
#RequestMapping(value="/destory/{id}")
public void destoryOuType(HttpServletRequest request,
HttpServletResponse response, #PathVariable("id") Long id){
log.info("destory");
ouTypeRepository.delete(id);
log.info("end");
}
………
……
}
Now when i click the delete button, the request url is: http://localhost:7080/MyApp.model.OuType/49 404 Not Found.
The expected url is http://localhost:7080/CSQMS/role/destory/49
How can i delete the model?
Rest proxy is special in that that it does not CRUD operations to urls, as you set in api, but to HTTP verbs: GET, POST, PUT, DELETE, etc. So api config is most likely ignored. Configure the proxy this way:
proxy:{
type:'rest',
url:'/CSQMS/role'
}
If your server expects that CRUD operation verb is part of the URL you probably need a different proxy or you need to implement buildUrl method.
I want to say i'm so careless, i should write 'destroy' instead of 'destory'. MY GOD.