Spring RESTful web-service returns 404 AFTER url is successfully called - json

I have a Spring MVC 4 app with Spring Security 4 and is deployed on Tomcat 8 running under jdk 1.8. The web-service has the controller defined as such:
#RequestMapping(value = "/build", method = RequestMethod.POST, produces = "application/json", headers =
{ "Accept=*/*", "Content-Type=*/*" })
public SubjectEntity build(#RequestBody SubjectImportDTO subjectImportDTO)
{
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User)
{
user = ((User) principal);
}
SubjectEntity entity = service.build(subjectImportDTO);
System.out.println("FINISH: build");
return entity;
}
I am getting a csrf token, I have that setup correctly. I know the url is getting called correctly because I can see that in the logs when I get there. The service on the back-end is running, data is correctly entered into the database, I correctly get the write object, and using the Jackson Mapper, the object 'SubjectEntity' should be translated into JSON and sent back to the requesting client. This web-service has been unit tested under the Spring Web Test framework, and it works great!
So, I am familiar with an HTTP 404 error in not finding a URL when the wrong parameters are passed in, or you're trying to do a POST when it's a GET, etc. So many reasons why we can get a 404 error ...
BUT ... IN THIS CASE ... We've already gotten to the URL, executed the code, and then it has the data it needs. Since the Controller says we have content-type / and it produces application/json, I don't know what else could be wrong?
Any ideas?

You should add #ResponseBodyto your method. without this, Spring mvc tries to find another handler method which can send a response.
NB: #RestController automatically add #ResponseBody on each method in a controller.

Related

How to redirect to a client callback page after a successful authentication via Identityserver4?

I am creating a login server using Identityserver4. I am using ASP.net core 3.1 for functionality, and angular 9 project for serving static files for login/registeration screens. The angular project is being served from within the .netcore project's wwwroot folder.
My flow goes like this
javascript client calls OIDC user manager's signInRedirect() method with following configurations
This sends a call to my Login method to render the angular's login component. Once the user fills in credentials, a second call is sent to my Login method return this.http.post('Account/Login', {UserName, Password, ReturnUrl}, {headers, responseType:'text'});
On successfull login, I do a return Redirect(model.returnUrl);
[HttpGet]
public IActionResult Login(string returnUrl)
{
return Redirect("/login?ReturnUrl=" + WebUtility.UrlEncode(returnUrl));
}
[HttpPost]
public IActionResult Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = _userManager.FindByNameAsync(model.UserName).Result;
var result = _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false).Result;
if(result.Succeeded)
{
return Redirect(model.ReturnUrl);
}
return Ok(new Error { ErrorMessage = "Invalid credentials" });
}
return Ok(new Error { ErrorMessage = "Some error" });
}
In my network tab, I can see that the return Url which is a call to IdentityServer's authorization endpoint /connect/authorize/callbackis successfull.
It has also made a second call to the actual javascript client in point 1 with the authentication successfull.
However, the problem arises here. This request is returning the HTML as string of the JS clients callback.html instead of actually redirecting to that URL(http://localhost:5003/callback.html)
I don't even have any way to access the URL of the returned HTML, otherwise I would have done a window.location.href. How do I capture the URL of the callback page in angular and redirect to it ?
I would need a few more details to remedy this particular situation. However, I did want to offer my expertise in the form of explaining how this is supposed to work. I have an NPM library imng-auth0-oidc that does this very thing, except that it uses NGRX.
Your callback.html should be a static (non-Angular) HTML page. You can find a copy here callback.html. The purpose of this page is to receive the OAUTH2 response and store the token in localStorage, then redirect the response to your Angular application. Once the application is loaded, you'll now have access to your token that is waiting in localStorage.
-Isaac

How can I default my spring boot application to application/xml without killing /health?

I'm working on a spring-boot (1.4.0-RELEASE) MVC Groovy app which will present an XML api. By default Spring seems to wire up Jackson which marshalls my response objects to JSON, however I want it to default to responding in XML without requiring any Accept header from clients, hence I configured the default content type as follows:
#Configuration
class SpringWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
}
This works just fine, however when running our tests I discovered that calling /health now returns a 406 status code and no content (it previously returned a 200 and a JSON response).
Having reverted the above change I thought perhaps I could force each controller to explicitly set the response content type via the use of a ResponseEntity, in doing so I tried the following in my controller method:
#RequestMapping(value = "/blah",
method = RequestMethod.GET)
ResponseEntity<MyResponseObject> getProgrammeRestrictions(#PathVariable String coreNumber) {
// Generate response object (code snipped)...
new ResponseEntity<MyResponseObject>(myResponseObject,
new HttpHeaders(contentType: MediaType.APPLICATION_XML),
HttpStatus.OK)
}
However this doesn't seem to influence the response type, which still defaults to JSON.
In a nutshell it seems that setting a default non-json content type breaks the actuator healthcheck. Is there someway to force the healthcheck bits and bobs to disregard the default setting and always be generated in JSON?
Has anyone else experienced this? Grateful for any pointers as I'm a bit stuck here.
Many thanks,
Edd
You need to add jackson-dataformat-xml dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Then, make its XmlMapper available:
#Autowired
private MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter;
#Bean
public ObjectMapper objectMapper(){
// this returns an XmlMapper, which is a subclass of ObjectMapper
return mappingJackson2XmlHttpMessageConverter.getObjectMapper();
}
It works when sending a request from the browser (http://localhost:8080/health), the returned result is in XML (chrome sends the header Accept: */*).
When sending the request programmatically, you still have to pass Accept: application/json in your header since the service expects this media type, but the returned result will be XML.

Should a server always send a JSON object as an http response?

I'm working on a node.js server using express and a android native app, using Retrofit 1.9.
For a login API that returns only a true/false answer to the client, should JSON still be used?
As I see it, the server has only to send a status code response:
if(isLegal) {
res.sendStatus(200);
dbConnector.updateUser(token);
}
else{
console.log('Token is not legal');
res.sendStatus(403);
}
But the Retrofit framework tries to convert the response to JSON, which makes me think I must send a JSON object with the answer, though it seems weird.
My retrofit restClient:
public class RestClient {
private static final String URL = SessionDetails.getInstance().serverAddress;
private retrofit.RestAdapter restAdapter;
private ServerAPI serverAPI;
public RestClient() {
restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(URL)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.build();
serverAPI = restAdapter.create(ServerAPI.class);
}
public ServerAPI getService() {
return serverAPI;
}
}
And usage:
restClient.getService().login(token.getToken(), token.getUserId(), new Callback<Void>() {
#Override
public void success(Void aVoid, Response response) {
Log.d("Chooser", "Successful login on server.");
}
#Override
public void failure(RetrofitError error) {
error.printStackTrace();
Log.d("Chooser", "Login failed on server.");
}
});
Using it as it is results with the following error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
There are many topics on this issue but no certain answer about the correct (or better) method to use.
Any ideas about the best implementation in these cases?
Sending an empty body with your HTTP response is perfectly legal and some clients may care only about the response status but some clients may expect to get a response so sending a body never hurts and sometimes may be useful.
You can include a JSON response in addition to the HTTP response status:
// Express 4.x:
res.status(403).json({error: 'Token is not legal'});
// Express 3.x:
res.json(403, {error: 'Token is not legal'});
Such an error message can be very useful for the client development. You can get 403 for many reasons, illegal token, expired token, a legal not expired token but for the wrong user that doesn't have some privilege - adding a specific error message in addition to the HTTP response code can tell the client what exactly went wrong and allows the client-side code to show a better error message to the user.
Also, note that true and false are also valid JSON.

JSON + Spring 3.1

Hi I am trying to write small app with REST Json. I have some method that returns ArrayList of entity objects. And I am doing that:
#RequestMapping(value="/workers/", method = RequestMethod.GET)
public #ResponseBody ArrayList<Workers> showAllEmployes() throws Exception
{
ArrayList<Workers> workers = new ArrayList<Workers>();
workers = (ArrayList<Workers>) spiroService.getAllWorkers();
return workers;
}
And after this I got:
HTTP Status 500. The server encountered an internal error that prevented it from fulfilling this request.
When I try to return primitive data type then all is ok. I have nothing in server logs. And I have necessary imports. Please some tip.
Seems you have issue in produce json format, try this.
#RequestMapping(value = "/workers/", method = RequestMethod.GET,
produces={MediaType.APPLICATION_JSON_VALUE})

Junit test for spring ws endpoint interceptor

Would appreciate any code examples of how to call a SpringWS endpoint intrceptor from a Junit test class. Particularly on how to prepare a SOAP message context and endpoint object. The SOAP message in the context will need to have a custom SOAP header included.
Something like....
public class MyInterceptorTest
private static String "... my XML SOAP test message ...";
#Test
public testMyInterceptor() {
myMessageContext = ... Build a MessageContext with the XML message string;
myEndPointObject = ... Build an endpoint object;
boolean result = MyInterceptorClass.handleRequest(myMessageContext, myEndPointObject);
... Check results;
}
Any examples would be appreciated.
The MessageContext can be created by instantiating a DefaultMessageContext object. The request WebServiceMessage can created using the test support class PayloadMessageCreator, but this only appeared in Spring-WS 2.x.
The endpoint object can be anything - it depends what your interceptor does with it. If it doesn't actually use it, then you can just pass in null.
I had the same issue and was able to figure it out in part using #skaffman's suggestion.
Basically, I had a custom EndpointInterceptor that I wanted to test with real data so that I would know I had everything correct.
You will have to upgrade spring-ws-test and other spring-ws dependencies to version 2.0 or higher. I ended up using something different than PayloadMessageCreator.
final Source payload = new StreamSource(new StringReader(soapPayload));
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
WebServiceMessage requestPayload = new SoapEnvelopeMessageCreator(payload).createMessage(saajSoapMessageFactory);
MessageContext messageContext = new DefaultMessageContext(requestPayload, saajSoapMessageFactory);
soapPayload is the string value of an entire soap envelope.
Something similar to this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
...fill in your custom headers here
</soapenv:Header>
<soapenv:Body><someRequest>...</someRequest></soapenv:Body>
</soapenv:Envelope>
You will obviously need to fill in your request payload, any namespaces, as well as your custom headers.
I set the endpoint object to null as I was not doing anything with it as part of my interceptor.