Why does JAX-RS doesn't support the 423 error code out-of-the-box? - jax-rs

I was using JAX-RS 2.1 and I found the Response class enum is missing the 423 error code.
Can anybody explain to me why?
I searched on the internet but not found any relevant thread.

From the Response.Status documentation:
Commonly used status codes defined by HTTP, see HTTP/1.1 documentation for the complete list. Additional status codes can be added by applications by creating an implementation of Response.StatusType.
The 423 status code is defined as part of WebDAV in the RFC 4918, which is an extension of the HTTP protocol.
Once 423 is registered in IANA, it can be considered a standard status code, but it's not defined in any of the documents that currently define the HTTP/1.1 protocol:
RFC 7230: Message Syntax and Routing
RFC 7231: Semantics and Content
RFC 7232: Conditional Requests
RFC 7233: Range Requests
RFC 7234: Caching
RFC 7235: Authentication
There's a number of ways to return 423 though. The simplest one is:
return Response.status(423).build();
Alternatively, you could implement Response.StatusType, as suggested in the Response.Status documentation:
public static class LockedStatusType implements Response.StatusType {
#Override
public int getStatusCode() {
return 423;
}
#Override
public String getReasonPhrase() {
return "Locked";
}
#Override
public Response.Status.Family getFamily() {
return Response.Status.Family.CLIENT_ERROR;
}
}
And then return it:
return Response.status(new LockedStatusType()).build();

Related

Is there a helper method to extract origin host from request (to build a link)

I receive request via a router :
#Bean
public RouterFunction<ServerResponse> routerFunction() {
return nest(path(API_PATH), route(GET("/"), indexHandler::getIndex));
}
handle by a method :
public Mono<ServerResponse> getIndex(ServerRequest request) {
...
}
I need to extract the url use to request the service, I have different cases, sometimes request are direct to service, sometimes request go through proxy (and add X-Forwarded-Path,X-Forwarded-Path headers).
Is there a helper method, to extract this details from ServerRequest object ?

MobileFirst 8: get client data ( IP address, request data ) in UserAuthenticationSecurityCheck

I'm trying to get some client data inside the UserAuthenticationSecurityCheck.validateCredentials method.
The IP Address is the most important for it.
In the other adapters, I'm using the HttpServletRequest:
#Context
protected HttpServletRequest request;
But this request object is always null in the UserAuthenticationSecurityCheck.
How can I get client data (IP Address or the headers) in this class?
You cannot inject the HttpServletRequest into a security check object(by design - not a bug). Once the user is authenticated, then you can make another Adapter Call, from where you can get the desired details. Unfortunately this is not documented anywhere (not to my knowledge at least).
I had a similar issue with AdapterAPI class as described here.
You can get request in security adapter but not from #Context.
Just override authorize method:
#Override
public void authorize(Set<String> scope, Map<String, Object> credentials, HttpServletRequest request, AuthorizationResponse response) {
//TODO use request object
super.authorize(scope, credentials, request, response);
}

Is it possible to possible to achieve with Jersey a polymorphic deserialisation of json payloads on POST resources

I'd like a endpoint to accept on a single verb and path a json payload that varies by a tiny bit and that can be mapped to different objects. The variation is usually on the field value like the grant_type in the OAuth world.
I scrapped stackoverflow and google on this, I found this among other
JAX-RS polymorphic POST request: how should I write my JSON?
Polymorphism in JSON, Jersey and Jackson
But those question don't seem to be relevant to me, jackson deserialisation alone works fine for this payloads, but Jersey always refuses to init the servlet with an error that let me thinks there's no way around it.
#Path("parent")
interface Resource {
#POST
#Path("test")
String test(Payload1 payload1);
#POST
#Path("test")
String test(Payload2 payload1);
#Data
#JsonTypeName("payload1")
class Payload1 extends BasePayload {
String a;
}
#Data
#JsonTypeName("payload2")
class Payload2 extends BasePayload {
String b;
}
// #JsonTypeInfo(use= Id.MINIMAL_CLASS, include=As.PROPERTY, property="#class")
#JsonTypeInfo(use= Id.NAME, include=As.PROPERTY, property="#payload")
#JsonSubTypes({
#JsonSubTypes.Type(value = Payload1.class),
#JsonSubTypes.Type(value = Payload2.class)
})
class BasePayload {
}
However I get this message in an exception upon servlet initialisation. (edited message for clarity)
</pre><p><b>root cause</b></p><pre>
org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during application initialization.
[[FATAL] A resource model has
ambiguous (sub-)resource method for HTTP method POST and input mime-types as defined by
"#Consumes" and "#Produces" annotations
at Java methods
public java.lang.String xxx.Service.test(xxx.Resource$Payload1)
and
public java.lang.String xxx.Service.test(xxx.Resource$Payload2)
at matching regular expression /test.
These two methods produces and consumes exactly the same mime-types and
therefore their invocation as a resource methods will always fail.;
source='org.glassfish.jersey.server.model.RuntimeResource#59b668bf']
Note however that having a endpoint with the parent class of the payload works, but you have to handle the dispatch yourself.
#POST
#Path("test")
String test(BasePayload payload);
I'm using Spring-Boot 1.4 / Jersey 2.23.2 / Jackson 2.8.5
The JAX-RS runtime uses the following for matching requests to resource methods:
URI: Defined in the #Path annotation.
Request method: Defined by a resource method designator such as #GET, #POST, etc).
Media type: Defined in Accept and Content-Type headers, that are matched with the values defined in the #Produces and #Consumes annotations, respectively.
The content of the payload is not taken into account, so it makes your first method definition ambiguous.
As you already figured out, the following approach is the way to go:
#POST
#Path("test")
String test(BasePayload payload);

WCF SOAP Service Ignore Content-Type charset

I have a new WCF service that some existing clients need to be able to communicate with.
There are some clients that are incorrectly sending the SOAP request with a Content-Type header of 'text/xml; charset=us-ascii'. At the moment, the clients themselves can't be changed.
When they send a request, they get the error message:
HTTP/1.1 415 Cannot process the message because the content type
'text/xml; charset=us-ascii' was not the expected type 'text/xml;
charset=utf-8'
Is there any way to instruct the WCF service to ignore the charset of the Content-Type and assume utf-8?
This may be helpful for you, if not I will delete the answer. I think this can be used in your context, but am not quite sure since there are no other details.
A WebContentTypeMapper can be used to override the content type being sent. This is snipped of the code used in the WCF Sample sets. In this example it is taking in whatever content type that is sent in and mapping it to json, but you could adjust that to your needs.
If you don't have it already you can download the samples from WCF Samples
This sample is located in ..WF_WCF_Samples\WCF\Extensibility\Ajax\WebContentTypeMapper\CS
using System.ServiceModel.Channels;
namespace Microsoft.Samples.WebContentTypeMapper
{
public class JsonContentTypeMapper : System.ServiceModel.Channels.WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
if (contentType == "text/javascript")
{
return WebContentFormat.Json;
}
else
{
return WebContentFormat.Default;
}
}
}
}

Content-Type case sensitivity in RESTEasy 3

I am developing a RestEasy client to connect to a 3rd party REST service which has defined its own custom media types. A made up example is
application/vnd.abc.thirdPartyThing-v1+json
Note the uppercase P in thirdParty.
I am using RESTEasy 3.0.11 for my client implementation. At the point where I make a POST call to the service my code looks like
Response response = target.request()
.post(Entity.<ThirdPartyThing>entity(
thing,
"application/vnd.abc.thirdPartyThing-v1+json"));
but RESTEasy sends to the server
Content-Type: application/vnd.abc.thirdpartything-v1+json
This is due to RESTEasy's MediaTypeHeaderDelegate class's toString() method, which lowercases the type and subtype MediaTypeHeaderDelegate. This should be correct, or at least unimportant, as RFC-1341 states that Content-Type values are case-insensitive - RFC-1341
Unfortunately the 3rd party service is checking the Content-Type in a case sensitive manner and so returning a 415 UNSUPPORTED MEDIA TYPE error. I've tested using curl which doesn't alter the content-type value and confirmed that it's a case issue. application/vnd.abc.thirdPartyThing-v1+json works, application/vnd.abc.thirdpartything-v1+json does not.
I'm in the process of raising a ticket, but in the meantime is there any way to override RESTEasy's default behaviour and send Content-Type headers without lowercasing the value?
Thanks for reading.
I could reproduce this behavior with RESTeasy 3.0.6.Final and would not expect it. Maybe you could check their JIRA if this has already been discussed or open an issue. I once had problems on the server side because a 2.x version of RESTeasy was checking the charset attribute of the Content-Type header case-sensitive. This was also changed.
You could solve this problem by a really ugly workaround: Overwrite the header again in a ClientRequestFilter.
public class ContentTypeFilter implements ClientRequestFilter {
private Map<String, String> contentTypes;
public ContentTypeFilter() {
contentTypes = new HashMap<>();
contentTypes.put("text/foo", "text/Foo");
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
String contentType = requestContext.getHeaderString("Content-Type");
if (contentTypes.containsKey(contentType)) {
requestContext.getHeaders().putSingle("Content-Type", contentTypes.get(contentType));
}
}
}
Don't forget to register this Filter:
Client client = ClientBuilder.newClient().register(ContentTypeFilter.class);