Printing remote pdf in Sencha ExtJs from Spring-Boot app doesn´t work - spring

I have an ExtJS app that shows a PDF from a spring boot app using a REST service. So far this works fine but when I have updated the spring-boot version from 1.3 -> 1.4, the code doesn´t work fine and shows me a blank PDF as response.
This is my code:
ExtJS - Sencha
Ext.Ajax.request({
url: MyApp.Application.Globals.baseUrl + url,
params: params,
method: 'POST',
async: false,
headers:{
'Authorization': Utils.getAuthorization()
},
scope : this,
// ON SUCCESS
success: function(response) {
window.open('data:application/pdf,' + escape(response.responseText));
},
// ON FAILURE
failure: function(err) {
console.log(err);
}
});
Server Code (Spring-Boot)
String report = reportService.executeReport(....);
response.setContentType("application/pdf");
try {
response.getWriter().write(report);
} catch (IOException e) {
ELogger.error(this, CoreConstants.LOGGER_CATEGORY, "error creating pdf", e);
}
POM
<spring-boot.version>1.4.4.RELEASE</spring-boot.version> with 1.3.3 it works fine
If anyone can help me, I will be grateful.
Regards!

I have found the workaround using ResponseEntity as a response:
#RequestMapping(value = "/example", produces = "application/pdf")
public ResponseEntity<InputStreamResource> exampleMethod(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT");
headers.add("Access-Control-Allow-Headers", "Content-Type");
headers.add("Content-Disposition", "filename=sysparamspdf");
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.setContentLength(report.getBytes(StandardCharsets.UTF_8).length);
ResponseEntity<InputStreamResource> response = new ResponseEntity<InputStreamResource>(
new InputStreamResource(stream), headers, HttpStatus.OK);
return response;
}

Related

Ionic HTTP request POST (works on postman) not working but GET works

I was working with localhost and my IONIC app is almost done until I decided to host it on 000webhost. I uploaded my Laravel API which is very basic (I used CORS middleware) then when I tested the app, the GET request works but POST and PUT doesn't.
Notes:
The URL is 100% correct because I'm using it on GET method
Data 100% compatible because I test it on Postman and it works
This.http is the http service:
this.http.getData().subscribe(s => {
console.log('Get Works');
this.data = s[0];
this.http.postData(this.data).subscribe(inf => {
console.log('Post works');
}, err => {
console.log(err)
console.log('Post dont work');
})
})
the http service
postData(data: any) {
let headers: HttpHeaders = new HttpHeaders();
headers.append("Access-Control-Allow-Origin", '*');
headers.append("Access-Control-Allow-Methods", 'POST, GET, OPTIONS, DELETE');
headers.append("Access-Control-Allow-Headers", '*');
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json,text/plain');
let requestOptions = { headers: headers }
return this.http.post(url, data)}
getData() {
let headers: HttpHeaders = new HttpHeaders();
headers.append("Access-Control-Allow-Origin", '*');
headers.append("Access-Control-Allow-Methods", 'POST, GET, OPTIONS, DELETE');
headers.append("Access-Control-Allow-Headers", '*');
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json,text/plain');
let requestOptions = { headers: headers }
return this.http.get(url, requestOptions)}
1
console
solution :
for some reason it works on localhost but not in 000webhost ...
it doesn't accept body/row request maybe changing the 'content-type' will make it work thought
however my solution is using HttpParams from angular :
postData(data: any) {
let headers: HttpHeaders = new HttpHeaders();
headers.append("Access-Control-Allow-Origin", '*');
headers.append("Access-Control-Allow-Methods", 'POST, GET, OPTIONS, DELETE');
headers.append("Access-Control-Allow-Headers", '*');
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json,text/plain');
const params = new HttpParams()
.set('type', data.type)
.set('email', data.email)
.set('uid', data.uid)
.set('lat', data.lat)
.set('lng', data.lng)
.set('city', data.city)
.set('municipality', data.municipality)
.set('subject', data.subject)
.set('description', data.description)
.set('image', data.image)
.set('upvote', data.upvote)
let requestOptions = { headers: headers, params: params }
return this.http.post(url, null, requestOptions)
}
return this.http
.post(url, data, { headers })

Empty HTTP response headers in browser but filled in POSTMAN

I've created a Spring Boot back end with a React front end.
When I'm sending a HTTP request via browser to my backend, I receive a
response with empty headers.
Browser response with empty headers
When I'm sending a HTTP request via the POSTMAN tool, I got filled headers!
POSTMAN response with fullfilled headers
That doesn't make sense to me.
Code: https://github.com/The-Taskmanager/SelfServiceWebwizard
Back end Mapping
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsernameOrEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
HttpHeaders headers = new HttpHeaders();
headers.add("Mustermann", "Max");
headers.add("Content-Type", "application/json");
headers.add("Authorization", new JwtAuthenticationResponse(jwt).getAccessToken());
return ResponseEntity.ok().headers(headers).body(new JwtAuthenticationResponse(jwt));
}
Front end request
login(username: string, password: string) {
fetch('http://localhost:8080/api/auth/signin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"usernameOrEmail": username,
"password": password
})
}).then(response => {
console.log(response);
console.log(response.text());
console.log(response.headers);
})
.catch(error => console.error(error));
}
UPDATE1:
Added this method but headers in browser still empty:
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
source.registerCorsConfiguration("/**", config);
return source;
}
UPDATE2:
When I request with React, Spring Boot shows me this ERROR:
ERROR 8876 --- [nio-8080-exec-4] c.s.jwt.JwtAuthenticationEntryPoint: Responding with unauthorized error. Message - full authentication is required to access this resource
Browser console:
When I request with Postman, Spring Boot shows no ERROR.
UPDATE3:
Request-header send by React:
{
host=[localhost:8080],
content-type=[application/json],
cache-control=[no-cache],
user-agent=[Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0],
accept-encoding=[gzip, deflate],
content-length=[58],
referer=[http://localhost:8080/],
origin=[http://localhost:8080],
dnt=[1],
connection=[keep-alive],
accept-language=[de,en-US;q=0.7,en;q=0.3],
accept=[*/*]
}
Request-header send by POSTMAN from POSTMAN console:
host:"localhost:8080"
content-type:"application/json"
cache-control:"no-cache"
user-agent:"PostmanRuntime/7.1.5"
accept-encoding:"gzip, deflate"
content-length:68
postman-token:"b00fd7a8-bd34-4a32-8681-990b04012e3b"
cookie:"JSESSIONID=2CAEC9280432DD13AABA53B73B7874AC"
accept:"*/*"
Solved by myself.
Changed code in front end's fetch method
console.log(response.headers)
// changed to:
console.log(response.headers.get('Authorization')
So the headers were there all the time but I couldn't manage to log them in browser console with React.
Nevertheless there is the Spring Boot (full authentication) and Browser (401) error in UPDATE2. Don't know where this is coming from.

Angular POST request's header is null

I am developing an Ionic 3 Mobile Application, I have problem with Angular's POST method.
In login page, I created a form and tried send data to server with Angular HTTP POST method. But in server (.NET WEB API) I see request's header is null.
Here is the Angular side codes;
login(username, password):Observable<Object>{
let url : string = this.apiUrl+"/login";
let headers = new HttpHeaders();
headers.append('Authorization', btoa(username+":"+password).toString());
return this.http.post(url,JSON.stringify({username,password}), {headers: headers});
}
Here is the .NET side codes for controller;
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
public Response Post()
{
return _mobileUserService.Login();
}
Here is the part of .NET side codes for catch request;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var token = request.Headers.GetValues("Authorization").FirstOrDefault();
}
catch (Exception ex)
{
}
return base.SendAsync(request, cancellationToken);
}
When request catched by .NET (in running), I see these values for "request" variable;
request = {Method: POST, RequestUri: 'http://localhost:41582/api/login', Version: 1.1, Content: System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent, Headers:
{
Connection: keep-alive
Accept: application/json
Accept: text/plain
Accept: */*
...
In normally, request's url is localhost:8100, so I think server accepted CORS
How can I solve that?
In Web api you have to tell which method is post or get based on how you have setup your route.
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
[HttpPost] // Decorate post this attribute in your controller
public Response Post()
{
return _mobileUserService.Login();
}

Enabling WebAPI CORS for Angular 2 authentification

I've seen a few answers on stackoverflow and I'm lost.
I have webapi 2 + standalone angular 2
webapi project is from template. the only thing i've changed is that i added CORS
and following line to IdentityConfig.cs > ApplicationUserManager Create()
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:3000" });
here I've all standard from template:
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
On the client side I have function to get access token, that works properly:
authenticate(loginInfo: Login): boolean {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post(this.baseUrl + 'Token', 'grant_type=password&username=alice2#example.com&password=Password2!',
{
headers: headers
})
.subscribe(
data => this.saveAuthToken(<AccessToken>(data.json())),
err => this.handleError(err),
() => console.log('authentication Complete')
);
return true;
}
And get function, that works ok without authentication (commented code) :
get(url: string) {
var jwt = sessionStorage.getItem(this.idTokenName);
var authHeader = new Headers();
if (jwt) {
authHeader.append('Authorization', 'Bearer ' + jwt);
}
return this.http.get(this.apiUrl + url, {
headers: authHeader
})
.map(res => res.json())
.catch(this.handleError);
//return this.http.get(this.apiUrl + url)
// .map(res => res.json())
// .catch(this.handleError);
}
But when i try to add Authorization header server returns:
XMLHttpRequest cannot load http://localhost:3868/api/values. Response for preflight has invalid HTTP status code 405
How to allow user to authenticate through Angular properly?
Install-Package Microsoft.Owin.Cors
Add to App_Start > Startup.Auth.cs > ConfigureAuth(IAppBuilder app)
app.UseCors(CorsOptions.AllowAll);
Only one line. That's all.
You could explicitly add the needed headers and methods:
context.Response.Headers.Add(
"Access-Control-Allow-Headers",
new[] { "Content-Type, Authorization" }
);
context.Response.Headers.Add(
"Access-Control-Allow-Methods",
new[] { "GET, POST, OPTIONS" }
);
I had to add the following to the globalasax.cs:
protected void Application_BeginRequest()
{
var req = HttpContext.Current.Request;
var res = HttpContext.Current.Response;
var val = res.Headers.GetValues("Access-Control-Allow-Origin");
if (val == null)
{
if (!req.Url.ToString().ToLower().Contains("token") || (req.Url.ToString().ToLower().Contains("token") && req.HttpMethod == "OPTIONS"))
{
res.AppendHeader("Access-Control-Allow-Origin", "http://localhost:4200");
}
}
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
res.StatusCode = 200;
res.End();
}
}
When talking to webapi angular and using a http post that either contains non-standard body contents (i.e json) or authentication then a pre-flight request is set that basically says 'am i okay to send the actual request'. Now there are several ways around this that essentially involve short cuts - use IE (if the server is on the same machine as IE ignores the port when deciding what the same machine is) or open CORS up to permit all (which is dangerous as the granting permission to an authenticated user opens your system up to all manner of hacks).
Anyway the solution we used was to add a method to the Globals.asax.cs on the server
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
var origin = HttpContext.Current.Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "content-type, withcredentials, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
Response.Flush();
}
}
Now the above is checking for the pre-flight very specifically and if it finds it it adds permissions to send the next request. On your system you may need to tweek the Allow_Headers request (easiest way is to use your browser f12 to look at what headers your pre-flight request is actually sending out.
Note that the above just deals with the pre-flight CORS will still apply for the actual http POST which will need correctly handling. For this we added the server we wanted to allow in to settings and then added the System.Web.Http.Cors to the WebApiConfig Register method as follows
var cors = new EnableCorsAttribute(Properties.Settings.Default.CORSOriginPermittedSite, "*", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
cors.SupportsCredentials = true;
config.EnableCors(cors);
This avoids hard coding the site which a production system really wants to avoid.
Anyway hopefully that will help.

PDF File is not getting downloaded. Ajax call failing with transfer-encoding as chunked

I am working on an application which allows the user to download a file on a button click. on click of a button I make an ajax call and that calls a spring controller. Inside that spring controller i have written a function as below:
#RequestMapping(value="/downloadFile", method=RequestMethod.GET)
public #ResponseBody String dowloadPDF(final HttpSession session, final HttpServletResponse response,#RequestParam(value="param1", required=true)final String param1,#RequestParam(value="param2",required=true)final String param2,#RequestParam(value="param3", required=true)final String param3,#RequestParam(value="fileName",required=true)final String fileNameRecieved){
final String fileName = fileNameRecieved.trim();
final String requestedFile = getPath(param1,param2,param3,fileName);
try{
File pdfFile= new File(requestedFile);
final InputStream inputStream = new FileInputStream(pdfFile);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition","attachment;filename="+fileName);
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}catch(Exception exception){
System.out.println("stckTrace : "+exception.getStackTrace());
exception.printStackTrace();
}
return "success";
}
and the ajax call written on a button click is as below.
$.ajax({
url : "downloadFile",
method : "GET",
dataType: "json",
contentType: 'application/pdf',
'Transfer-Encoding': 'chunked',
sync:true,
data:{
"param1" :param1,
"param2":param2,
"param3" :param3,
"fileName": fileName
},
success : function(response) {
alert(response);
},
error : function(e) {
alert('Error: ' + e.getAllResponseHeaders());
}
});
The problem is my controller is getting called and executed without giving any exceptions. but no file was not downloaded and also i am getting error in the response of ajax call stating that
"Server: Apache-Coyote/1.1
Content-Disposition: attachment;filename=GATE-CS 2004.pdf
Content-Type: application/pdf
Transfer-Encoding: chunked
Date: Thu, 05 Feb 2015 16:33:22 GMT
"
Any one can please help me on this, I have changed the content type in ajax call as well, still it is not working.
One problem seems to be related with dataType and contentType in the ajax call.
I think you have swapped their value in ajax call.As per your problem description ,their value should be.
dataType:'application/pdf','Transfer-Encoding': 'chunked'
contentType : "json"//but you don't need to use it,because you are not sending the json data

Resources