SpringMVC change JSP with post - spring-boot

I'm trying to update the user's current JSP on a post request. I am using #Controller annotation right now (I've also tried RestController)
Controller:
#PostMapping(value = { "postHome" })
public ModelAndView postHome(#RequestBody String body, ModelAndView model) {
JSONObject jsonObject = new JSONObject(body);
String customerName = jsonObject.getString("customerName");
model.setViewName("feasibility");
model.addObject("customerName", customerName);
return model;
}
JavaScript:
function postCustomer() {
var custName = {
"customerName": document.getElementById("customerName").value
};
fetch(sendURL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(custName)
});
}
It's posting to the server just fine (I've printed out the body), but nothing happens after that. No errors or anything. The JSP I am trying to update the client with is feasibility.jsp. I tried something earlier and it was trying to force my client to run a GET on localhost:8080/feasibility?customerName=Name but that's not what I was trying to do.
In the past I was able to just return "feasibility" as a String, but that doesn't work with this project (could be the versions I was using before).
Thanks!

Related

ASP.NET Core API response headers not in expected place

I have an ASP.NET Core API that adds two headers to its response callback_uri and redirect_uri.
The strange thing (to me) is that in my AJAX call to the service, the headers are part of the JSON data, as a headers array, rather than the request object itself. I cannot use jqxhr.getResponseHeader(...) and therefore must interrogate the headers array manually within the response data.
Because the StatusCode is also part of the data it means my AJAX success callback is always called, even when I'm testing for a 400 bad request response, which makes testing less simple.
Web API controller action:
[HttpGet, Route("Authenticate")]
public HttpResponseMessage Authenticate(string applicationId)
{
HttpResponseMessage response;
if(!_security.IsApplicationIdValid(applicationId))
{
response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
response.ReasonPhrase = ErrorMessages.INVALID_APPLICATION_ID;
return response;
}
IAuthenticationProvider authProvider = _security.GetAuthenticationProvider();
response = new HttpResponseMessage(System.Net.HttpStatusCode.Redirect);
response.Headers.Add(HeaderKeyNames.CALLBACK_URI_KEY_NAME, authProvider.GetCallbackUrl());
response.Headers.Add(HeaderKeyNames.AUTHENTICATION_SERVICE_REDIRECT_URI_KEY_NAME, authProvider.GetUrl());
return response;
}
AJAX code:
var settings = {
data: { "applicationId": applicationId },
success: successCallback, // at the moment just writes to console
error: errorCallback, // at the moment just writes to console
method: "GET"
};
$.ajax(url, settings);
Am I doing something wrong on the server-side?
You can use a combination of ResultFilters and ServiceFilterAttribute to add your custom headers. This is particularly useful because:
ServiceFilter enables you to have DI access in your ResultFilter.
You can apply it as an Attribute in the actions you want
You can test it.
Putting all together:
Create the custom result filter class
public class CustomHeadersResultFilter : IResultFilter
{
private readonly IMyService _myService;
public CustomHeadersResultFilter(IMyService myService)
{
_myService = myService;
}
public void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add("my-header", _myService.GetData());
// if under CORS, this need to be added otherwise you can't read the headers using xhr.getResponseHeader('my-header')
context.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-header");
}
public void OnResultExecuted(ResultExecutedContext context)
{
// can't add headers here, since it's too late in the pipeline
}
}
Register it in your Startup.ConfigureServices
services.AddTransient<IMyService, MyService>();
// our custom result filter
services.AddTransient<CustomHeadersResultFilter>();
Apply the attribute in the action you want to return the custom headers
[HttpGet("{id}")]
[ServiceFilter(typeof(CustomHeadersResultFilter))]
public ActionResult Get(string id)
{
if (id == "something-bad")
{
return BadRequest("invalid application id");
}
// return a 200 Ok. Check the other types if you want something different
return Ok();
}
Testing all of this with a separate web application, doing an ajax request to the API, you can access the headers:
<script>
var settings = { method: "GET" };
$.ajax('http://localhost:61284/api/values/test', settings)
.done(function (data, textStatus, xhr) {
alert(xhr.getResponseHeader('my-header'));
})
.fail(function () {
alert("error");
});
</script>
Add headers like this: (ofc change the type if needed or define your own)
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
What you're doing is creating a HttpResponseMessage object, serializing it to json and then returning it.
This is why the headers are in the json content, instead of the http response.
What you can do is someting like this:
[HttpGet, Route("Authenticate")]
public IActionResult Authenticate(string applicationId)
{
if(!_security.IsApplicationIdValid(applicationId))
{
return BadRequest(ErrorMessages.INVALID_APPLICATION_ID);
}
IAuthenticationProvider authProvider = _security.GetAuthenticationProvider();
this.Response.Headers.Add(HeaderKeyNames.CALLBACK_URI_KEY_NAME, authProvider.GetCallbackUrl());
this.Response.Headers.Add(HeaderKeyNames.AUTHENTICATION_SERVICE_REDIRECT_URI_KEY_NAME, authProvider.GetUrl());
return StatusCode(302);
}

Unsupported Media Type with Content-Type header?

I'm getting an inexplicable error when I try to post an element by using the API of my backend. The API returns an error with code 415, related to Media Type:
Failed to load resource: the server responded with a status of 415 ()
My backend returns me this error:
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
EDIT: Error with Guru solution:
Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain' not supported
The annoying thing is that I have added this header to my request:
Content-Type: application/json
And the body is correctly parsed into JSON format.
I am using Angular 5, and my backend has been developed by using Spring.
Angular client code:
postProject(project: Project) {
const headers: HttpHeaders = new HttpHeaders();
headers.append('Content-Type', 'application/json');
return this.http.post('http://localhost:8443/projects', project.body(), {headers: headers})
.map(response => response);
}
where body method is:
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return JSON.stringify(object);
}
Spring API code:
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addLocation(#RequestBody Project project) {
return new ResponseEntity<>(esProjectService.save(project), HttpStatus.OK);
}
Where RequestMapping of the class is /projects:
#RestController
#RequestMapping("/projects")
public class ProjectResource {
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addLocation(#RequestBody Project project) {
return new ResponseEntity<>(esProjectService.save(project), HttpStatus.OK);
}
... OTHER METHODS...
}
I've already gone through this, one way to solve it is to specify the type #RequestPart (value = "nameOfResource" and consumes = {"multipart/form-data"}
Do not forget to specify the name of Content in Angular.
I hope it helps.
Here is an example below:
RequestMapping(value = "/add", method = RequestMethod.POST, consumes = {"multipart/form-data"}, produces = "application/json")
public ResponseEntity<?> add(#RequestPart(value = "image", required = true) MultipartFile image,
#RequestPart(value = "team", required = true) #Valid Team team, BindingResult bResult) {
}
In angular 5 HttpHeaders is immutable. Therefore, you should use it like this
let headers = new HttpHeaders({
'Content-Type': 'application/json',
'X-XSRF-TOKEN': token
});
or
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('X-XSRF-TOKEN', token);
Set the headers in this way and it should resolve your issue. I have put the sample code just to explain how you should add multiple headers. Don't put 'X-XSRF-TOKEN' if you don't require it.
I think the mistake comes from the fact that you want to consume application / json while you send plain / text.
I explain, in your service you specify
#RequestMapping(value = "", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
while in your front code you send plain / text
return JSON.stringify(object);
Just delete the JSON.stringify call and return the json object and all is well.
Change
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return JSON.stringify(object);
}
to
body() {
const object = {
id: this.id,
name: this.name,
'num_execs': this.num_execs
};
return object;
}

Required request part 'file' is not present - Angular2 Post request

I am trying to get my file upload functionality done using Angular2 and SpringBoot. I can certify that my java code for the file uploading working fine since I have tested it successfully using Postman.
However, when it comes to sending the file from Angular2 front end, I am getting the HTTP 400 response saying Required request part 'file' is not present.
This is how I send the POST request from Angular2.
savePhoto(photoToSave: File) {
let formData: FormData = new FormData();
formData.append('file', photoToSave);
// this will be used to add headers to the requests conditionally later using Custom Request Options
this._globals.setRequestFrom("save-photo");
let savedPath = this._http
.post(this._endpointUrl + "save-photo", formData)
.map(
res => {
return res.json();
}
)
.catch(handleError);
return savedPath;
}
Note that I have written a CustomRequestOptions class which extends BaseRequestOptions in order to append Authorization header and Content Type header. Content Type header will be added conditionally.
Following is the code for that.
#Injectable()
export class CustomRequestOptions extends BaseRequestOptions {
constructor(private _globals: Globals) {
super();
this.headers.set('X-Requested-By', 'Angular 2');
this.headers.append('virglk', "vigamage");
}
merge(options?: RequestOptionsArgs): RequestOptions {
var newOptions = super.merge(options);
let hdr = this._globals.getAuthorization();
newOptions.headers.set("Authorization", hdr);
if(this._globals.getRequestFrom() != "save-photo"){
newOptions.headers.set('Content-Type', 'application/json');
}else{
//request coming from save photo
console.log("request coming from save photo");
}
return newOptions;
}
}
This conditional header appending is working fine. The purpose of doing that is if I add 'Content-Type', 'application/json' header to every request, file upload method in Spring controller will not accept it. (Returns http 415)
Everything seems to be fine. But I get Required request part 'file' is not present error response. Why is that? I am adding that param to the form Data.
let formData: FormData = new FormData();
formData.append('file', photoToSave);
This is the Spring Controller method for your reference.
#RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo", consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadPhoto(#RequestParam("file") MultipartFile file){
if (file.isEmpty()) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("DEBUG: Attached file is empty");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
String returnPath = null;
try {
// upload stuff
} catch (IOException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(e.getMessage());
return new ResponseEntity<ErrorResponse> (errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<String>(returnPath, HttpStatus.OK);
}
EDIT - Adding the payload of the request captured by the browser
As you can see, the param "file" is available there.
Try to add
headers: {
'Content-Type': 'multipart/form-data'
},
to your
.post(this._endpointUrl + "save-photo", formData)
Change formData.append('file', photoToSave);
to              formData.append('file', this.photoToSave, this.photoToSave.name); and also add headers specifying the type of data you are passing to API, in your case it will be 'Content-Type': 'multipart/form-data'. Post the output here if it fails even after changing this.
Is there a chance that you're using zuul in a secondary app that is forwarding the request? I saw this with an update where the headers were stripped while forwarding a multi-part upload. I have a gatekeeper app which forwards requests using zuul to the actual service via a looking from eureka. I fixed it by modifying the url like this:
http://myserver.com/service/upload
to
http://myserver.com/zuul/service/upload
Suddenly the 'file' part of the upload header was no longer stripped away and discarded.
The cause, I suspect was a re-try mechanism which cached requests. On failure, it would re-submit the requests, but somehow for file uploads, it wasn't working properly.
To upload a file to the server, send your file inside a FormData and set content type as multipart/form-data.
export const uploadFile = (url, file, onUploadProgress) => {
let formData = new FormData();
formData.append("file", file);
return axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data;charset=UTF-8',
// other headers
},
onUploadProgress,
})
};
To handle file object, be careful with consumes attribute and #RequestPart annotation here.
#PostMapping(value = "/your-upload-path", consumes = "multipart/form-data")
public ResponseEntity<Object> uploadFile(#RequestPart("file") #Valid #NotNull #NotBlank MultipartFile file) {
// .. your service call or logic here
}

Why is asp.net webAPI always returning text/html?

I would like to create webservices returning json. However, I'm always getting 'text/html' as the responses content type.
First shot:
public StringContent Get()
{
List<Cell> list = new List<Cell>();
Cell c = new Cell("Cell1");
Cell c2 = new Cell("Cell2");
list.Add(c);
list.Add(c2);
return new StringContent(
Newtonsoft.Json.JsonConvert.SerializeObject(list),
Encoding.UTF8,
"application/json");
}
Responsecontent: System.Net.Http.StringContent
second shot:
public List<Cell> Get()
{
Cell c = new Models.Cell("Cell1");
List<Cell> list = new List<Cell>();
list.Add(c);
return list;
}
Responsecontent: System.Collections.Generic.List`1[TestApp.Models.Cell]
This is how I access the endpoint:
$.ajax({
url: "http://localhost:54787/Cell/Get",
type: "GET",
contentType:"application/json",
accepts: {
text: "application/json"
},
success: function (response) {
$("#result").html(JSON.parse(response));
},
error: function (xhr, status) {
alert("error");
}
});
If you have no good reason to do serialization manually, you should use Web API default mechanism by returning object instead of StringContent. For example, you can change your method to return List<Cell> directly.
public List<Cell> Get()
{
// return List<Cell> just like you write a typical method
}
This way, you will not get text/html anymore. However, you will still get XML in Chrome. It is because Chrome's default HTTP Accept header contains application/xml, and it is supported by default in Web API. If you have no need to support XML result, so you can remove it by the following code during startup (maybe in Global.asax)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
PS: If you don't know whether you need XML or not, then you don't need it.

Spring MVC4 Session Management

I am developing a small spring project in which the login page has two type of users admin and staff. on login attempt i want to apply session using spring MVC4 and also wants to open jsp based on the user role(Admin or Staff).
the session has four/five fields like name,id,role,SessionId. i want these information to travel through the jsp pages. But i don't want to do this using url parameter passing.
I don't know how to do this because i am new in spring and this is my first project. Help me Please.
If someone can provide me the sample code and guide me on this then it would be very helpfull.
// Login.jsp code
var MobileNo=$('#mobno').val();
var StaffPwd=$('#pwd').val();
$.ajax
(
{
url: "http://localhost:8080/OnlineStore/kmsg/grocery/Login",
type: "POST",
data: {MobileNo: MobileNo,StaffPwd: StaffPwd},
success: function(data)
{
var vUserRole = data["UserRole"];
var vUserName = data["UserName"];
if(data==="")
{
alert("Login Failed");
}
else
{
if(vUserRole == "Admin")
{
alert("Login Success: " + vUserName);
window.location.href = "http://localhost:8080/OnlineStore/JspPages/City.jsp";
}
if(vUserRole == "CityAdmin")
{
alert("Login Success: " + vUserName);
window.location.href = "http://localhost:8080/OnlineStore/JspPages/Locality.jsp";
}
if(vUserRole == "Staff")
{
alert("Login Success: " + vUserName);
window.location.href = "http://localhost:8080/OnlineStore/JspPages/CustomerOrder.jsp";
}
}
},
error: function(e)
{
alert('Error:' +e)
}
}
);
// this is controller code
#RequestMapping("/Login")
public #ResponseBody UserServiceModel selectStaff(#RequestParam Map<String,String> requestParams) throws Exception
{
String MobileNo = requestParams.get("MobileNo");
String StaffPwd = requestParams.get("StaffPwd");
return staffAdapter.login(MobileNo, StaffPwd);
}
--------------
Just store your data in some serializable object. For e.g.:
public class SessionData implements Serializable {
private String name;
private String id;
private String role;
//etc..
}
then set this object as the session attribute when the user first logs in:
session.setAttribute("sessionData", new SessionData(name, id, role, etc...))
To access this object in a JSP page you can use: ${sessionScope['sessionData']}, to access a specific field:
${sessionScope['sessionData'].id}
${sessionScope['sessionData'].name}
${sessionScope['sessionData'].role}
To access these attributes in JavaScript files just define some page data in your JSP page, for e.g.:
<script type="text/javascript">
pageData = {
id: "${sessionScope['sessionData'].id}",
name: "${sessionScope['sessionData'].name}",
role: "${sessionScope['sessionData'].role}"
}
</script>
and in js file reference them via:
pageData.id
pageData.name
pageData.role
I can see that you trying to create roles based application. You are already using Spring MVC, any reason why not use Spring Security along with it? I would highly advice doing so as it will make your life much easier.

Resources