JAX-RS : HTTP Status 415 - Unsupported Media Type - jax-rs

I got this error when making an ajax request to my WebService :
HTTP Status 415 - Unsupported Media Type
I tried to add the good MediaType (Text/Html, i think), but it doesn't work. I have still this error. What could this be, do you think ?
Thank you !
My request :
$(document).on('submit','.form-add-edit',function(e){
e.preventDefault();
var idDisruptive = $(e.target).find('input[name=idDisruptive]').val();
var url = "api/disruptive";
var method = "POST";
if (idDisruptive){
url = url + '/' + idDisruptive;
method = "PUT";
}
$.ajax({
url: url,
method : method,
data : getDisruptiveParams(),
success : function (response){
console.log('EDIT')
console.log(response);
//editDisruptive(response);
},
error : function(response){
console.log('EDIT ERROR')
console.log(response);
}
});
});
The Web Service :
#Stateless
#Path("disruptive")
#Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON})
public class DisruptiveFacadeREST extends AbstractFacade<Disruptive> {
#PersistenceContext(unitName = "StoryGeneratorPU")
private EntityManager em;
public DisruptiveFacadeREST() {
super(Disruptive.class);
}
#POST
#Override
public void create(Disruptive entity) {
super.create(entity);
}
#PUT
#Path("{id}")
public void edit(#PathParam("id") Integer id, Disruptive entity) {
super.edit(entity);
}
#Override
protected EntityManager getEntityManager() {
return em;
}
}

You need to set the content-type on the jQuery request. If you don't, it will default to application/x-www-form-urlencoded. And just because you add #Consumes(MediaType.APPLICATION_FORM_URLENCODED) doesn't mean that JAX-RS will not how to convert the form data to Disruptive. There need to be a MessageBodyReader to handle that conversion, which there isn't. Same goes for MediaType.TEXT_HTML. Just adding that means nothing if there is no converter to handle the conversion. Remove those two. What you want is to handle JSON conversion, and there should already be a MessageBodyReader included in the EE server that will convert JSON data to arbitrary POJOs.
So for the jQuery, just add
$.ajax({
contentType: 'application/json'
})
That should solve the problem.

Related

ASP.NET Core 2.1 API POST body is null when called using HttpWebRequest, seems it can't be parsed as JSON

I'm facing a strange bug, where .NET Core 2.1 API seems to ignore a JSON body on certain cases.
I advised many other questions (e.g this one, which itself references others), but couldn't resolve my problem.
I have something like the following API method:
[Route("api/v1/accounting")]
public class AccountingController
{ sometimes it's null
||
[HttpPost("invoice/{invoiceId}/send")] ||
public async Task<int?> SendInvoice( \/
[FromRoute] int invoiceId, [FromBody] JObject body
)
{
// ...
}
}
And the relevant configuration is:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new TestJsonConverter());
})
.AddJsonFormatters()
.AddApiExplorer();
// ...
}
Where TestJsonConverter is a simple converter I created for testing why things doesn't work as they should, and it's simple as that:
public class TestJsonConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return token;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary (would be neccesary if used for serialization)");
}
}
Calling the api method using Postman works, meaning it goes through the JSON converter's CanConvert, CanRead, ReadJson, and then routed to SendInvoice with body containing the parsed json.
However, calling the api method using HttpWebRequest (From a .NET Framework 4, if that matters) only goes through CanConvert, then routes to SendInvoice with body being null.
The request body is just a simple json, something like:
{
"customerId": 1234,
"externalId": 5678
}
When I read the body directly, I get the expected value on both cases:
using (var reader = new StreamReader(context.Request.Body))
{
var requestBody = await reader.ReadToEndAsync(); // works
var parsed = JObject.Parse(requestBody);
}
I don't see any meaningful difference between the two kinds of requests - to the left is Postman's request, to the right is the HttpWebRequest:
To be sure, the Content-Type header is set to application/json. Also, FWIW, the HttpWebRequest body is set as follows:
using(var requestStream = httpWebRequest.GetRequestStream())
{
JsonSerializer.Serialize(payload, requestStream);
}
And called with:
var response = (HttpWebResponse)request.GetResponse();
Question
Why does body is null when used with HttpWebRequest? Why does the JSON converter read methods are skipped in such cases?
The problem was in the underlying code of the serialization. So this line:
JsonSerializer.Serialize(payload, requestStream);
Was implemented using the default UTF8 property:
public void Serialize<T>(T instance, Stream stream)
{
using(var streamWriter = new StreamWriter(stream, Encoding.UTF8) // <-- Adds a BOM
using(var jsonWriter = new JsonTextWriter(streamWriter))
{
jsonSerializer.Serialize(jsonWriter, instance); // Newtonsoft.Json's JsonSerializer
}
}
The default UTF8 property adds a BOM character, as noted in the documentation:
It returns a UTF8Encoding object that provides a Unicode byte order
mark (BOM). To instantiate a UTF8 encoding that doesn't provide a BOM,
call any overload of the UTF8Encoding constructor.
It turns out that passing the BOM in a json is not allowed per the spec:
Implementations MUST NOT add a byte order mark (U+FEFF) to the
beginning of a networked-transmitted JSON text.
Hence .NET Core [FromBody] internal deserialization failed.
Lastly, as for why the following did work (see demo here):
using (var reader = new StreamReader(context.Request.Body))
{
var requestBody = await reader.ReadToEndAsync(); // works
var parsed = JObject.Parse(requestBody);
}
I'm not very sure. Certainly, StreamReader also uses UTF8 property by default (see remarks here), so it shouldn't remove the BOM, and indeed it doesn't. Per a test I did (see it here), it seems that ReadToEnd is responsible for removing the BOM.
For elaboration:
StreamWriter and UTF-8 Byte Order Marks
The Curious Case of the JSON BOM

How do I hook into micronaut server on error handling from a filter?

For any 4xx or 5xx response given out by my micronaut server, I'd like to log the response status code and endpoint it targeted. It looks like a filter would be a good place for this, but I can't seem to figure out how to plug into the onError handling
for instance, this filter
#Filter("/**")
class RequestLoggerFilter: OncePerRequestHttpServerFilter() {
companion object {
private val log = LogManager.getLogger(RequestLoggerFilter::class.java)
}
override fun doFilterOnce(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>>? {
return Publishers.then(chain.proceed(request), ResponseLogger(request))
}
class ResponseLogger(private val request: HttpRequest<*>): Consumer<MutableHttpResponse<*>> {
override fun accept(response: MutableHttpResponse<*>) {
log.info("Status: ${response.status.code} Endpoint: ${request.path}")
}
}
}
only logs on a successful response and not on 4xx or 5xx responses.
How would i get this to hook into the onError handling?
You could do the following. Create your own ApplicationException ( extends RuntimeException), there you could handle your application errors and in particular how they result into http error codes. You exception could hold the status code as well.
Example:
class BadRequestException extends ApplicationException {
public HttpStatus getStatus() {
return HttpStatus.BAD_REQUEST;
}
}
You could have multiple of this ExceptionHandler for different purposes.
#Slf4j
#Produces
#Singleton
#Requires(classes = {ApplicationException.class, ExceptionHandler.class})
public class ApplicationExceptionHandler implements ExceptionHandler<ApplicationException, HttpResponse> {
#Override
public HttpResponse handle(final HttpRequest request, final ApplicationException exception) {
log.error("Application exception message={}, cause={}", exception.getMessage(), exception.getCause());
final String message = exception.getMessage();
final String code = exception.getClass().getSimpleName();
final ErrorCode error = new ErrorCode(message, code);
log.info("Status: ${exception.getStatus())} Endpoint: ${request.path}")
return HttpResponse.status(exception.getStatus()).body(error);
}
}
If you are trying to handle Micronaut native exceptions like 400 (Bad Request) produced by ConstraintExceptionHandler you will need to Replace the beans to do that.
I've posted example here how to handle ConstraintExceptionHandler.
If you want to only handle responses itself you could use this mapping each response code (example on #Controller so not sure if it works elsewhere even with global flag:
#Error(status = HttpStatus.NOT_FOUND, global = true)
public HttpResponse notFound(HttpRequest request) {
<...>
}
Example from Micronaut documentation.
Below code I used for adding custom cors headers in the error responses, in doOnError you can log errors
#Filter("/**")
public class ResponseCORSAdder implements HttpServerFilter {
#Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return this.trace(request)
.switchMap(aBoolean -> chain.proceed(request))
.doOnError(error -> {
if (error instanceof MutableHttpResponse<?>) {
MutableHttpResponse<?> res = (MutableHttpResponse<?>) error;
addCorsHeaders(res);
}
})
.doOnNext(res -> addCorsHeaders(res));
}
private MutableHttpResponse<?> addCorsHeaders(MutableHttpResponse<?> res) {
return res
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET")
.header("Access-Control-Allow-Credentials", "true");
}
private Flowable<Boolean> trace(HttpRequest<?> request) {
return Flowable.fromCallable(() -> {
// trace logic here, potentially performing I/O
return true;
}).subscribeOn(Schedulers.io());
}
}

Request.IsAjaxRequest() alternative in MVC6

I am trying to run this example Rendering Partial Views using ajax, but i got the following compilation error:
'HttpRequest' does not contain a definition for 'IsAjaxRequest' and no extension method 'IsAjaxRequest' accepting a first argument of type 'HttpRequest' could be found.
public ActionResult ItemsList(string ID)
{
Item item = Service.GetItemById(ID);
if (Request.IsAjaxRequest())
{
return PartialView("viewPath", item);
}
else
{
return View("viewPath", item);
}
}
Check the user agent, as this:
var isAjax = Request.Headers["X-Requested-With"] == "XMLHttpRequest";
Ricardo Peres's answer works for ajax requests but misses the new Fetch types. This works for me:
internal static class RequestHelpers
{
internal static bool IsAjaxRequest(this HttpRequest request)
{
return string.Equals(request.Query["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(request.Headers["X-Requested-With"], "Fetch", StringComparison.Ordinal);
}
}

Customize binding in ASP.NET Web Api

I am stuck with following problem in ASP.NET Web Api. Let say I have following code in my ApiController:
public void Post(Person person)
{
// Handle the argument
}
What I would like to do is to accept following JSON request:
{
"person": {
"name": "John Doe",
"age": 27
}
}
I would like to go around creating some holding object for each model just to properly bind incoming data. In previous version of MVC, it was possible to define something like Prefix to solve this.
Let me report that I have been able to solve this implementing CustomJsonMediaTypeFormatter:
public class EmberJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
public override System.Threading.Tasks.Task<object> ReadFromStreamAsync(
Type type,
System.IO.Stream readStream,
System.Net.Http.HttpContent content,
IFormatterLogger formatterLogger)
{
return base.ReadFromStreamAsync(
typeof(JObject),
readStream,
content,
formatterLogger).ContinueWith<object>((task) =>
{
var data = task.Result as JObject;
var prefix= type.Name.ToLower();
if (data[prefix] == null)
{
return GetDefaultValueForType(type);
}
var serializer = JsonSerializer.Create(SerializerSettings);
return data[prefix].ToObject(type, serializer);
});
}
}
and replacing default JsonMediaTypeFormatter in GlobalConfiguration.

Using #Consume with GET request in Jersey Rest

I'm trying to bind values in a GET request to a POJO.
The values are parameters in a HTTP GET request. I'm using JSONP to pass the parameters however it looks like JSONP pushes the JSON object up onto the Request line so its not really a JSON object which is being sent but instead just name value pairs on the URL.
Is it possible to map the values in my GET request to a POJO? Jersey gives the following exception when i try binding
A HTTP GET method, public void handleJSONP(MyPojo), should not consume any entity.
The binding code is looking in the request body however it doesnt exist because it is a GET request. Is there any other way to bind the values in the request without having to manually include a #QueryParam entry for each ?
Thanks
I was able resolve this by using #com.sun.jersey.api.core.InjectParam of jersey
public JSONWithPadding doSomething(#InjectParam final MyPojo argPojo)
Then the Pojo looks like this
public class MyPojo
{
/** */
#QueryParam("value1")
private String value1;
/** */
#QueryParam("value2")
private String value2;
/** */
#QueryParam("value3")
private List<String> value3;
HTTP GET by specification includes the parameters in the URL - therefore it only accepts value pairs. So, what you are trying to do is not feasible. why don't you use a POST instead to bundle a JSON object together with the request?
I am proposing a more expanded example.
jQuery client side:
var argPojo = {
callback:"myPojoCallback",
value1:"val1",
value2:"val2",
value3:["val1", "val2", "val3"]
};
var url = 'xxx.xx.xx.xx/testPojo';
$.ajax({
type: 'GET',
async: false,
jsonpCallback: argPojo.callback,
url: url,
data:argPojo,
contentType: "application/json",
dataType: 'jsonp',
beforeSend:function(){
console.log("sending:",argPojo);
},
success: function(response) {
console.log("reciving",response);
},
error: function(e) {
console.error("error",e);
}
});
on the server
#Path("testPojo")
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces("application/x-javascript")
public JSONWithPadding testPojo(#InjectParam MyPojo argPojo){
System.out.println(argPojo);
System.out.println(argPojo.callback);
System.out.println(argPojo.value1);
System.out.println(argPojo.value2);
System.out.println(argPojo.value3);
return new JSONWithPadding(argPojo, argPojo.callback);
}
the actual class object
public class MyPojo {
#QueryParam("callback")
public String callback;
#QueryParam("value1")
public String value1;
#QueryParam("value2")
public String value2;
#QueryParam("value3[]")
public List<String> value3;
public MyPojo(){}
}
chrome console result
sending: Object
callback: "myPojoCallback"
value1: "val1"
value2: "val2"
value3: Array[3]
__proto__: Object
receiving Object
callback: "myPojoCallback"
value1: "val1"
value2: "val2"
value3: Array[3]
__proto__: Object
As we know GET request cannot consume any entity, we need to pass each parameter as params. To be simple we can do like the below using javax.ws.rs.BeanParam
(We can use the #BeanParam instead of #InjectParam
public JSONWithPadding doSomething(#BeanParam final MyPojo argPojo)
....
public class MyPojo
{
/** */
#QueryParam("value1")
private String value1;
/** */
#QueryParam("value2")
private String value2;
/** */
#QueryParam("value3")
private List<String> value3;
GET request cannot consume any entity.
Instead, use POST or PUT methods (provided request is for insert or update).
Otherwise, go with standard way of passing attributes in URL.