What is the javax.ws.rs MediaType we need to use for JWT response - jersey

What is the MediaType we can use in #Produces annotation if we are expecting the server to return a JWT response?
for application json we can use,
#Produces({MediaType.APPLICATION_JSON})
But what if we want it to be application/jwt ?
If we use it as below server will return an error saying "No message body writer has been found for response class ...."
#Produces({"application/jwt"})

I would try something like this:
#GET
#Produces("application/jwt")
public Response authenticate(...) {
String jwt = ...;
return Response.ok(jwt).build();
}
This should work.

Related

How to set at Spring Boot all Errors to json?

Is there at spring boot a configuration possible, which returns all errors in a json format?
For example 404, or 401. Need to replace this 404 page with just json.
Many thanks
This is since by default, springboot produces the error as html.
To get the Json output, add produces argument as follows so that the content returned will be for sure in json format.
#RequestMapping(....., produces = "application/json")
You can custom your error controller to handle it:
#RestController
public class CustomErrorController extends BasicErrorController {
private final Logger logger = LoggerFactory.getLogger(CustomErrorController.class);
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
// let all MediaType return json data
#RequestMapping(consumes = MediaType.ALL_VALUE)
public ResponseEntity<Map<String, Object>> allError(HttpServletRequest request, HttpServletResponse response) {
return super.error(request);
}
}
You mean using #ControllerAdvice with the content header set to application/json

Supporting application/json and application/x-www-form-urlencoded simultaneously from Spring's rest controller

Am writing a REST endpoint which needs to support both application/x-www-form-urlencoded and application/json as request body simultaneously. I have made below configuration,
#RequestMapping(method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE }, consumes = {
MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE }, path = Constants.ACCESS_TOKEN_V1_ENDPOINT)
public OAuth2Authorization createAccessTokenPost(
#RequestBody(required = false) MultiValueMap<String, String> paramMap) { ..
While it supports application/x-www-form-urlencoded or application/json individually (when I comment out one content type from consumes = {}), but it does not support both simultaneously. Any ideas ?
So RestControllers by default can handle application/json fairly easily and can create a request pojo from a #RequestBody annotated parameter, while application/x-www-form-urlencoded takes a little more work. A solution could be creating an extra RestController method that has the same mapping endpoint to handle the different kinds of requests that come in (application/json, application/x-www-form-urlencoded, etc). This is because application/x-www-form-urlencoded endpoints need to use the #RequestParam instead of the #RequestBody annotation (for application/json).
For instance if I wanted to host a POST endpoint for /emp that takes either application/json or application/x-www-form-urlencoded as Content-Types and uses a service to do something, I could create Overload methods like so
#Autowired
private EmpService empService;
#PostMapping(path = "/emp", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public ResponseEntity createEmp(final #RequestHeader(value = "Authorization", required = false) String authorizationHeader,
final #RequestParam Map<String, String> map) {
//After receiving a FORM URLENCODED request, change it to your desired request pojo with ObjectMapper
final ObjectMapper mapper = new ObjectMapper();
final TokenRequest tokenRequest = mapper.convertValue(map, CreateEmpRequest.class);
return empService.create(authorizationHeader, createEmpRequest);
}
#PostMapping(path = "/emp", consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity createEmp(final #RequestHeader(value = "Authorization", required = false) String authorizationHeader,
final #RequestBody CreateEmpRequest createEmpRequest) {
//Receieved a JSON request, the #RequestBody Annotation can handle turning the body of the request into a request pojo without extra lines of code
return empService.create(authorizationHeader, createEmpRequest);
}
As per my findings, spring does not support content types "application/x-www-form-urlencoded", "application/json" and "application/xml" together.
Reason I figured: Spring processes JSON and XML types by parsing and injecting them into the java pojo marked with #RequestBody spring annotation. However, x-www-form-urlencoded must be injected into a MultiValueMap<> object marked with #RequestBody. Two different java types marked with #RequestBody will not be supported simultaneously, as spring may not know where to inject the payload.
A working solution:
"application/x-www-form-urlencoded" can be supported as it is in the API. That is, it can be injected into spring's MultiValueMap<> using an #RequestBody annotation.
To support JSON and XML on the same method, we can leverage servlet specification and spring's class built on top of them to extract the payload as stream.
Sample code:
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.MultiValueMap;
// usual REST service class
#Autowired
private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
#Autowired
private Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter;
public ResponseEntity<Object> authorizationRequestPost(HttpServletResponse response, HttpServletRequest request,#RequestBody(required = false) MultiValueMap<String, String> parameters) {
// this MultiValueMap<String,String> will contain key value pairs of "application/x-www-form-urlencoded" parameters.
// payload object to be populated
Authorization authorization = null;
HttpInputMessage inputMessage = new ServletServerHttpRequest(request) {
#Override
public InputStream getBody() throws IOException {
return request.getInputStream();
}
};
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
authorization = (Authorization) mappingJackson2HttpMessageConverter.read(Authorization.class, inputMessage);
}
else if (request.getContentType().equals(MediaType.APPLICATION_XML_VALUE)) {
authorization = (Authorization)jaxb2RootElementHttpMessageConverter.read(Authorization.class, inputMessage);
}
else{
// extract values from MultiValueMap<String,String> and populate Authorization
}
// remaining method instructions
}
Point to note that any custom data type/markup/format can be supported using this approach. Spring's org.springframework.http.converter.HttpMessageConverter<> can be extended to write the parsing logic.
Another possible approach could be an AOP style solution which would execute the same logic: parse payload by extracting it from HttpServlet input stream and inject into the payload object.
A third approach will be to write a filter for executing the logic.
It's not possible to handle application/json and application/x-www-form-urlencoded requests simultaneously with a single Spring controller method.
Spring get application/x-www-form-urlencoded data by ServletRequest.getParameter(java.lang.String), the document said:
For HTTP servlets, parameters are contained in the query string or posted form data.
If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method.
So, if your method parameter is annotated with #RequestBody, Spring will read request body and parse it to the method parameter object. But application/x-www-form-urlencoded leads Spring to populate the parameter object by invoking ServletRequest.getParameter(java.lang.String).
Just to make it, the above answer doesn't work as even if you do not annotate MultiValueMap with #RequestBody it would always check for contentType==MediaType.APPLICATION_FORM_URLENCODED_VALUE which again in rest of the cases resolves to 415 Unsupported Media Type.

getting http 415, Unsupported Media Type using text/xml

I have a jersey endpoint(JAX-RS) that I'm trying to hit with a text/xml req. I'm getting back an http 415 and I don't understand why. Here is the info. Any ideas? Thanks.
#Path("/bid")
#Produces("text/xml;charset=ISO-8859-1")
#Consumes({"text/xml", "application/xml"})
#Resource
public class BidController {
#RolesAllowed("blah")
#POST
public Response bid(final HttpServletRequest request) {
I am hitting it via Postman(REST client) and sending {"Content-Type":"text/xml"}
My POST body is definitely well formed xml.
You are getting a 415 response because JAX-RS does not know how to convert incoming XML into a HttpServletRequest.
If you really want access to the request, then you need to annotate it with #javax.ws.rs.core.Context:
#RolesAllowed("blah")
#POST
public Response bid(#Context final HttpServletRequest request) {
However, as you say you're hitting it with text/xml, then you may actually want:
#POST
public Response bid(final MyRequest request) {
...
}
where MyRequest is declared something like:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyRequest {
#XmlElement
int field1;
#XmlElement
String field2;
...
}
which corresponds to XML like:
<MyRequest>
<field1>11327</field1>
<field2>some string
</MyRequest>
The JAX-RS specification requires implementations to be able to decode incoming text/xml and encode outgoing text/xml via JAXB.

Jersey get credentials from url

I have an url like this:
http://log:pass#localhost:8080/myendpoint
And Jersey endpoint:
#GET #Produces(MediaType.APPLICATION_JSON) #Path("/login")
#Consumes(MediaType.APPLICATION_JSON) public Response login(
#Context HttpHeaders headers, #QueryParam("callback") String callback)
{
}
And ideally I want to get 'log' and 'pass' in my endpoint method. How to do that in Jersey? I tried many endpint method signatures, filters, etc but it shows me
http://localhost instead of http://log:pass#localhost everywhere
How to achieve this?
Assuming that in your front end, you are sending your parameters as a json object using JSON.stringify() then back in your endpoint method. Add this as a second argument to that method signature JsonObject payload. Then you can access your query parameters within that method as follows
String log = payload.getString("log");
String pass = payload.getString("pass");
Revised Version
#Path("/login")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response login(#Context UriInfo uriInfo, #Context HttpHeaders headers, #QueryParam("callback") String callback) {
URI requestUri = uriInfo.getRequestUri();
String authority = requestUri.getAuthority(); // authority contains what you need;
}

Using Jersey's #BeanParam results in a 415 error

I am trying to use Jersey's #BeanParam annotation the following way:
This is my bean:
public class BeanParamModel {
#QueryParam(value = "param1")
private String param1;
public BeanParamModel(#QueryParam("param1") String param1) {
this.param1 = param1;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}}
And this is the resource method that needs to use it:
#Consumes("*/*")
#Path("mypath")
#GET
public Response getUpgradeStatus(#QueryParam("param1") String param1, #BeanParam BeanParamModel user) {
return Response.ok().build();
}
Now I want to test this using a unit test which sends an http request to a test server with the following url:
GET http://path_to_resource?param1=1
My problem is that results in a 415 response with Jersey printing this message:
A message body reader for Java class BeanParamModel, and Java type class BeanParamModel, and MIME media type application/octet-stream was not found.
The registered message body readers compatible with the MIME media type are:...
I've trying adding a "application/x-www-form-urlencoded" header but the message repeats for that header type as well. I also tried using an application/json header, this results in EOF expection from the jackson mapper due to end of input.
Can anyone tell me what I'm not doing correctly? from the jersey documentation of #BeanParam it seems pretty simple.
With a #GET you should not have #Consumes.

Resources