I'm trying to download a file from WebApi using ExtJs 4.2.3. After reading Extjs 4 downloading a file through ajax call, it looks like my best bet is to use the standard form.submit, however, my data is not passing as expected to the WebApi controller - using the below code, items comes through null.
Controller
public HttpResponseMessage Post(string exportType, List<PcAvailableComponent> items)
{
var dataToExport = _service.ExportItems(exportType, items);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new MemoryStream(dataToExport);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Standard Submit
expForm.submit({
url: AppRootUrl + 'api/AdminDataExport?exportType=' + exportType,
//this doesn't work
jsonData: items,
waitMsg: 'Generating export...',
success: function (form, action) {
if (typeof expWindow !== "undefined") {expWindow.close();}
},
failure: function(form, action) {
Ext.Msg.alert('Failed', 'An error has occurred while generating the export: ' + JSON.parse(action.response.responseText)['ExceptionMessage']);
}
});
Ajax submit (works but can't get file back)
Ext.Ajax.request({
url: AppRootUrl + 'api/AdminDataExport',
params: {
exportType: 'PricingAndCosting'
},
jsonData: items,
method: 'POST',
success: function (response) {
expWindow.close();
console.log("success!");
}
});
Ended up abandoning WebApi for this controller, and just passing the JSON as a string, then deserializing it on the server side:
[HttpPost]
public ActionResult Index(string exportType, string items)
{
var dataToExport = _service.ExportItems(exportType, JsonConvert.DeserializeObject<List<PcAvailableComponent>>(items));
Response.AddHeader("Content-Disposition", "inline; filename=" + exportType + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx");
return File(dataToExport, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
Related
I spent a couple of days on this problem and read hundreds posts. But i still can't resolve it.
REST Controller:
#RequestMapping(value = "/goodInStocks/loadlist={id}", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadList(#PathVariable("id") Integer id) {
byte[] bytes = pdfService.loadList(id);
String filename = "report.pdf";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/pdf"));
headers.setContentLength(bytes.length);
headers.setContentDispositionFormData("inline", filename);
if (bytes.length == 0) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
Angular Service:
function getLoadList(id){
var deferred = $q.defer();
$http.get(REST_SERVICE_URI+'loadlist='+id, {responseType : 'arraybuffer'})
.then(
function (response) {
deferred.resolve(response);
},
function(errResponse){
console.error('Error while fetching load list');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
Angular Controller:
function loadList(documentId){
GoodInStockService.getLoadList(documentId)
.then(
function(d){
var file = new Blob([d.data], {type: 'application/pdf'});
var url = window.URL || window.webkitURL;
var fileURL = url.createObjectURL(file);
window.open(fileURL);
},
function(errResponse){
console.error('Error while getting Load list');
}
)
}
Finally, I get new tab in browser with next error "Failed to load PDF document"
I tried to use different headers in rest controllers, create Blob from response, add 'produces="application/pdf' property in rest controller's method(by the way, in this way i got 406 error - why?)Also, i detect that arraybuffer(if i don't set length in header) and byte[] have different length, is it normal?
Try to write directly to response and flush/close.
RequestMapping(value = "/goodInStocks/loadlist={id}", method = RequestMethod.GET)
public void loadList(#PathVariable("id") Integer id, HttpServletResponse response) {
byte[] byteArray= pdfService.loadList(id);
response.setStatus(HttpStatus.SC_OK);
OutputStream os = response.getOutputStream();
os.write(byteArray);
os.flush();
os.close();
}
Implemented webapi routing and having two different route methods for for retrieving values but it is differentiated by supplying parameter type.
Api methods are getting hit for the corresponding action methods if we simply specify "apiurl/api/contact/search/sri" and "apiurl/api/contact/get/2" in direct browser url.
But when comes to communicate with angular to webapi, api is not getting hit.
//angular service
contact.search = function (inputName) {
return $http({
method: 'GET',
url: url + 'api/contact/search',
//params: { name: inputName }
data: { name: inputName }
});
//return $http.get(url + 'api/contact/search', name);
}
//WebAPI
[HttpGet]
[Route("search/{name:alpha}")]
public IHttpActionResult GetContacts([FromBody]string name)
{
repository = new ContactRepository();
if (string.IsNullOrEmpty(name))
{
var message = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("Search Name can not be empty")
};
throw new HttpResponseException(message);
}
return Ok(repository.GetContact(name));
}
// GET api/contact/5
[HttpGet]
[Route("get/{id:int}")]
public IHttpActionResult Get(int id)
{
repository = new ContactRepository();
if (id == 0)
{
var message = new HttpResponseMessage(HttpStatusCode.NotFound) { Content = new StringContent("Issue with Passed Id Parameter.") };
throw new HttpResponseException(message);
}
return Ok(repository.GetContact(id));
}
When you use data: { name: inputName }, it is appended to the url in the following way:
...api/contact/search?name=inputName
but what you want is this:
...api/contact/search/inputName
So, you have two options.
Either change your angular code:
return $http({
method: 'GET',
url: url + 'api/contact/search/' + inputName,
});
or change your API to accept QUERY params.
Hope it helps
I am using webApi and have generated the model using entityframework the overload method of the GET(int id) I am trying to call that using the query of the $resource
I am trying to pass an optional parameter to a call using the $resource but get the error [$resource:badcfg] I have had a google and people say add
{
'get': {method: 'GET'},
'query': {method: 'GET', isArray: true}
}
into the function, so I have tried this: but still have no luck.
function minorResource($resource, appSettings) {
return $resource(appSettings.serverPath + "/api/minorworks/:id",
{
'get': {method: 'GET'},
'query': {method: 'GET', isArray: true}
});
}
Would you use 2 separate methods or can the above function be made to work?
For completness here is my Controller call
minorResource.query({id: vm.seachCriteria}, function (data) {
//console.log(data);
vm.minorWork = data;
});
Note that query is used to retrieve an array of objects and get is used to retrieve a single object. That means that with a get you usually sent the id of the object to the API.
So in your case:
var minorWorksResource = $resource(appSettings.serverPath + "/api/minorworks/:id");
// Note that query returns an array.
var works = minorWorksResource.query(function() {
var firstWork = works[0];
});
// Note that we pass an ID that will be fetched in the query string.
var singleWork = minorWorksResource.get({id: 123}, function() {
});
And the WebAPI part:
[RoutePrefix("api/minorworks")]
public class MinorWorksController : ApiController {
public IHttpActionResult Get(int id) {
var singleWork = null;
// Retrieve a single item;
return Ok(singleWork);
}
public IHttpActionResult Get() {
var workList = null;
// Retrieve the whole list.
return Ok(workList );
}
}
Me and my m8s are developing a manuscript handling system for our university using Spring MVC,angularJS etc. We have some issues with deleting a user from the database.
We get always HTTP Status 400 - Required String parameter 'userName' is not present
type Status report
message Required String parameter 'userName' is not present
description The request sent by the client was syntactically incorrect.
Controller:
#Secured({ "ROLE_ADMIN" })
#RequestMapping(value = "/delete/{userName}", method = RequestMethod.DELETE)
public void deleteUser(#RequestParam String userName) {
LOGGER.info("Deleted user: " + userName);
userManagerService.deleteUser(userName);
}
Method of the ManuscriptAdminService.js:
function deleteUser(userName){
$log.info("Delete selected user "+new Date());
$http.delete('delete/'+userName).then(function(data){
console.log(data);
},function(error){
$log.error("Error occured while admin tried to delete user "+new Date());
});
}
Method of the ManuscriptAdminController.js
vm.showModalUserDelete = function(index) {
$log.info("Show user delete modal "+new Date());
var modelInstance = $modal
.open({
animation : true,
templateUrl : 'htmlcontent/content/admin/modal/userDeleteManageModal.html',
controller : 'ManuscriptAdminModalinstacneController',
controllerAs : 'ManuscriptAdminModalinstacneController',
size : 300,
resolve : {
items : function() {
return ManuscriptAdminService.getUserName(index);
}
}
});
modelInstance.result.then(function (result) {
ManuscriptAdminService.deleteUser(result);
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
You're using a URI template variable in /delete/{userName}, so you will need to use #PathVariable annotation on your parameter:
#Secured({ "ROLE_ADMIN" })
#RequestMapping(value = "/delete/{userName}", method = RequestMethod.DELETE)
public void deleteUser(#PathVariable String userName) {
LOGGER.info("Deleted user: " + userName);
userManagerService.deleteUser(userName);
}
i'm working on a project like this:
(HTML Forms(AJAX)+ twitter bootstrap)(solo HTML, no JSP,etc..)->Servlets(on Google App Engine-JAVA)->Persistence(Google Cloud SQL).
I'm quite new to jQuery ajax calls, but i understand the process, as i'm used to write the old XHR code.
Below is the function in JS, that does not write to console the expected result..so far most of the times form data is persisted.
My Servlet if fine, and outputs a valid JSON.(calling the URL on a browser always works as expected.)
My answer is why jQuery ajax callbacks(done,fail,always) aren't working properly? They do write to console/display alert().
THANKS, for your time!
$(document).ready(function() {
var myEmail = "";
var myGender = "";
$('#saveButton').click(function() {
$('#myform').submit();
//alert('Handler for .submit() called.');
myEmail = document.getElementById("inputEmail").value;
window.console.log('EMAIL---->' + myEmail);/*ok log!*/
//alert('EMAIL->' + myEmail);
var radioObj = document.forms['myForm'].elements['gender'];
myGender = getCheckedValue(radioObj);
window.console.log('GENDER---->' + myGender);/*ok log!*/
//alert('GENDER->' + myGender);
var jqXHR = $.ajax({
statusCode : {
404 : function() {
alert("404 ERROR - page not found");
}
},
url : "/newuser",
type : "GET",
timeout : 10000,
data : {
email : myEmail,
gender : myGender,
operation : '0'
},
done : function(data, textStatus, jqXHR) {
window.console.log('done -> RESPONSE---->' + data);/*this does not log!*/
alert(data);
},
fail : function(jqXHR, textStatus, errorThrown) {
window.console.log('always -> RESPONSE---->' + data); /*this does not log!*/
alert(data);
},
always : function(data, textStatus, jqXHR) {
window.console.log('always -> RESPONSE---->' + data); /*this does not log!*/
alert(data);
}
});
});
});
done, fail and always are not properties of the settings object passed to $.ajax, they are callbacks on the jqxhr object returned by the call to $.ajax. They should be configured like this instead:
var jqxhr = $.ajax( "example.php" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });
Check out the API documentation for further usage guidance.