Does StringContentProvider set Content-Type header in HTTP request? - http-headers

I am trying to use Firebase Cloud Messaging by Google with the help of Jetty HTTP client:
public static final String FCM_URL = "https://fcm.googleapis.com/fcm/send";
public static final String FCM_KEY = "key=AAAA....";
private final HttpClient mHttpClient = new HttpClient();
private final CompleteListener mFcmListener = new CompleteListener() {
#Override
public void onComplete(Result result) {
if (result.isFailed()) {
// TODO delete FCM token in database for certain responses
}
}
};
mHttpClient.start();
mHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.content(new StringContentProvider(notificationStr), "application/json")
.send(mFcmListener);
My question is very simple, but I couldn't find the answer myself yet by looking at the StringContentProvider and its base classes -
If I need to set the request HTTP header for FCM:
Content-Type: application/json
then do I have to add the line:
.header(HttpHeader.CONTENT_TYPE, "application/json")
or will that class already do it for me?

A couple of points:
Yes, if you don't set content type header explicitly, it would be auto set based on the selected Content Provider.
By default, the StringContentProvider sets Content-Type to text/plain. To override, you need to use another constructor -
new StringContentProvider("application/json", content, StandardCharsets.UTF_8);
Request #setContent method auto sets Content-Type header to the provided value. Hence, you need not make any change to the code.

Related

spring amqp RPC copy headers from request to response

I'm looking for a way to copy some headers from the request message to the response message when I use RabbitMq in RPC mode.
so far I have tried with setBeforeSendReplyPostProcessors but I can only access the response and add headers to it. but I don't have access to the request to get the values I need.
I have also tried with the advice chain, but the returnObject is null after proceeding so I can't modify it (I admit I don't understand why it is null... I thought I could get the object to modify it):
#Bean
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer simpleRabbitListenerContainerFactoryConfigurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = new SimpleRabbitListenerContainerFactory();
simpleRabbitListenerContainerFactory.setAdviceChain(new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object returnObject = invocation.proceed();
//returnObject is null here
return returnObject;
}
});
simpleRabbitListenerContainerFactoryConfigurer.configure(simpleRabbitListenerContainerFactory, connectionFactory);
return simpleRabbitListenerContainerFactory;
}
a working way is to change my method annotated with #RabbitListener so it returns a Message and there I can access both the requesting message (via arguments of the annotated method) and the response.
But I would like to do it automatically, since I need this feature at different places.
Basicaly I want to copy one header from the request message to the response.
this code do the job, but I want to do it through an aspect, or an interceptor.
#RabbitListener(queues = "myQueue"
, containerFactory = "simpleRabbitListenerContainerFactory")
public Message<MyResponseObject> execute(MyRequestObject myRequestObject, #Header("HEADER_TO_COPY") String headerToCopy) {
MyResponseObject myResponseObject = compute(myRequestObject);
return MessageBuilder.withPayload(myResponseObject)
.setHeader("HEADER_RESPONSE", headerToCopy)
.build();
}
The Message<?> return type support was added for this reason, but we could add an extension point to allow this, please open a GitHub issue.
Contributions are welcome.

Header values getting lost spring integration

Im using header enricher which uses existing headers to set a value of new header. However existing header information is lost and only 3 header remain ie request-id,timestamp and raw-body.
public String vipul(Message<String> message) {
MessageHeaders messageHeaders =message.getHeaders();
if (messageHeaders.containsKey("x-death")) {
List<HashMap<String, Object>> deathList = (List<HashMap<String, Object>>) messageHeaders
.get("x-death");
//logger.debug(message.get("messageId")+" "+deathList);
if (deathList.size() > 0) {
HashMap<String, Object> death = deathList.get(0);
if (death.containsKey("original-expiration")) {
return (String) death.get("original-expiration");
//logger.info(messageHeaders.get("messageId")+" original-expiration = "+death.get("original-expiration"));
}
}
} else {
return null;
}
return "";
}
In this messageHeaders map has only has 3 keys and not all the header keys which are normally there. I need to make a retry system using original expiration .
MY spring integration xml has following snippet :
<int:header-enricher input-channel="fromPushAppointmentErrorHandler1"
output-channel="fromPushAppointmentErrorHandler">
<int:header name="original_expiration" method="vipul" ref="errorhelper"/>
</int:header-enricher>
First of all it looks like you also need an overwrite="true" for that <int:header name="original_expiration"> since the logic in your vipul() is about to produce a new value for existing header and that is not going to happen since the value is already there in headers.
The fact that you are missing some headers in this your logic might be dictated by some upstream <transformer> which returns the whole Message without copying request headers.

Response pipeline

I came across a difficulty while was working with Asp.net core 1.0 RTM. For example in case bellow we will see output result as "-Message_1--Message_5-":
public class MessageMiddleware
{
private readonly RequestDelegate _next;
private readonly IApplicationBuilder _app;
public MessageMiddleware(RequestDelegate next, IApplicationBuilder app)
{
_next = next;
_app = app;
}
public async Task Invoke(HttpContext context)
{
var started1 = context.Response.HasStarted;//false
await context.Response.WriteAsync("-Message_1-");
var test = true; // will hit this line
var started2 = context.Response.HasStarted;//true
await context.Response.WriteAsync("-Message_5-");
await _next.Invoke(context);
}
}
But in this case (header "Content-Type" was added) the result will be only "-Message_1-" and execution is really stopped:
public class MessageMiddleware
{
private readonly RequestDelegate _next;
private readonly IApplicationBuilder _app;
public MessageMiddleware(RequestDelegate next, IApplicationBuilder app)
{
_next = next;
_app = app;
}
public async Task Invoke(HttpContext context)
{
var started1 = context.Response.HasStarted;//false
await context.Response.WriteAsync("-Message_1-");
var started2 = context.Response.HasStarted;//true
context.Response.ContentType = "text/html";
var test = true; // will NOT hit this line
var started3 = context.Response.HasStarted;//will NOT hit this line
await context.Response.WriteAsync("-Message_5-"); //will NOT hit this line
await _next.Invoke(context);
}
}
I found only this remark in official documentation:
Avoid modifying HttpResponse after invoking next, one of the next components in the pipeline may have written to the response, causing it to be sent to the client.
and this question at SO: Why can't the HttpResponse be changed after 'next' call?
But it's not enough to understand interaction with props of HttpContext.Response during middleware pipeline and how this interection affects on final result - headers and body content of HttpResponse.
Could somebody explain general behaviour of processing response by ASP.NET core? For example, when response headers are send to client and how setting HttpContext.Response properties(headers, body content) affects on this?
When pipeline inside(outside) middliware is terminated?
Thank you!
As a general rule, when the client makes a request to the server, it gets back a response. That response contains headers and a body. The headers contain many pieces of information about the response like the content type, encoding/compression used, cookies, etc. Here is an example of the headers sent back by the live.asp.net site as seen in the chrome developer tools:
The other part of the response is the body. It often contains html or json. Here is a screenshot of the body for the same response:
The easiest way to think about it is to think of these two being sent together to the client, first the headers then the body. So as a developer, your only opportunity to set any value on the response object that affects the headers is up to the point at which you start sending the body. One you begin sending the body of the response you can no longer change the headers because they are sent as the first part of the response just before the body begins sending.
That's why #tseng said "Don't set headers after you have written something to the response stream".
If a developer isn't familiar with http headers they might not realize that context.Response.ContentType = "text/html" is changing a header, but under the hood, that's exactly what it is doing. Likewise, setting a cookie changes a response header under the hood. In general, if you are changing some property of the response object you should ask yourself "will this change an http header?" and if the answer is "yes" then you need to do it before you make a call to Response.WriteAsync.

RestSharp RestResponse is truncating content to 64 kb

Hi I am using the RestSharp to create the request to my web API. Unfortunately the response.content does not contain full response, which I am able to see when I perform request through browser or fiddler. The content is being truncated to 64 kb. I am attaching my code below.
Could you please advice what could solve this issue?
var request = new RestRequest("Products?productId={productId}&applicationId={applicationId}", Method.GET);
request.RequestFormat = DataFormat.Json;
request.AddParameter("productId", id, ParameterType.UrlSegment);
request.AddParameter("applicationId", Settings.ApplicationId, ParameterType.UrlSegment);
request.AddHeader("X-AppKey", token.AppKey);
request.AddHeader("X-Token", token.Token);
request.AddHeader("X-IsWebApi", "true");
RestResponse response = (RestResponse) client.Execute(request);
if (response.StatusCode == HttpStatusCode.Found)
{
// The following line failes because response.Content is truncated.
ShowProductModel showProductModel =
new JavaScriptSerializer().Deserialize<ShowProductModel>(response.Content);
// Do other things.
return ShowProductApi(showProductModel, q, d, sort, breadcrumb);
}
This is happening because RestSharp uses the HttpWebRequest class from the .NET Framework. This class has a static attribute called DefaultMaximumErrorResponseLength. This attribute determines the max length of an error response, and the default value for this attribute is 64Kb.
You can change the value of that atribbute before instatiating the RestRequest class.
Here's some code:
HttpWebRequest.DefaultMaximumErrorResponseLength = 1048576;
var request = new RestRequest("resource" + "/", Method.POST)
{
RequestFormat = DataFormat.Json,
JsonSerializer = new JsonSerializer()
};
That way your error response can be longer without problemns.
It looks like HttpStatusCode.Found may be causing the issue. That equates to Http Status Code 302 which is a form of redirect. I'm not entirely sure if that's necessarily the right thing to do in this case. If you have "found" the data you are looking for you should return a success level status code, e.g. 200 (Ok). Wikipedia has a list of HTTP Status Codes with summaries about what they mean and links off to lots of other resources.
I've created a little demonstrator solution (You can find it on GitHub) to show the difference. There is a WebApi server application that returns a list of values (Hex codes) and a Console client application that consumes the resources on the WebApi application.
Here is the ValuesFound resource which returns HTTP Status Code 302/Found:
public class ValuesFoundController : ApiController
{
public HttpResponseMessage Get(int count)
{
var result = Request.CreateResponse(HttpStatusCode.Found, Values.GetValues(count));
return result;
}
}
And the same again but returning the correct 200/OK response:
public class ValuesOkController : ApiController
{
public HttpResponseMessage Get(int count)
{
var result = Request.CreateResponse(HttpStatusCode.OK, Values.GetValues(count));
return result;
}
}
On the client side the important part of the code is this:
private static void ProcessRequest(int count, string resource)
{
var client = new RestClient("http://localhost:61038/api/");
var request = new RestRequest(resource+"?count={count}", Method.GET);
request.RequestFormat = DataFormat.Json;
request.AddParameter("count", count, ParameterType.UrlSegment);
RestResponse response = (RestResponse) client.Execute(request);
Console.WriteLine("Status was : {0}", response.StatusCode);
Console.WriteLine("Status code was : {0}", (int) response.StatusCode);
Console.WriteLine("Response.ContentLength is : {0}", response.ContentLength);
Console.WriteLine("Response.Content.Length is: {0}", response.Content.Length);
Console.WriteLine();
}
The count is the number of hex codes to return, and resource is the name of the resource (either ValuesOk or ValuesFound) which map to the controllers above.
The console application asks the user for a number and then shows the length of response for each HTTP Status Code. For low values, say 200, both versions return the same amount of content, but once the response content exceeds 64kb then the "Found" version gets truncated and the "Ok" version does not.
Trying the console application with a value of about 9999 demonstrates this:
How many things do you want returned?
9999
Waiting on the server...
Status was : OK
Status code was : 200
Response.ContentLength is : 109990
Response.Content.Length is: 109990
Status was : Redirect
Status code was : 302
Response.ContentLength is : 109990
Response.Content.Length is: 65536
So, why does RestSharp do this? I've no idea why it truncates content in one instance and not in the other. However, it could be assumed that in a situation where the server has asked the client to redirect to another resource location that content exceeding 64kb is unlikely to be valid.
For example, if you use Fiddler to look at what websites do, the responses in the 300 range (Redirection) such as 302/Found do have a small content payload that simply contain a little HTML so that the user can click the link to manually redirect if the browser did not automatically redirect for them. The real redirect is in the Http "Location" header.

Spring Data Rest : How to expose a json schema from a repository (2.0.0.M1)

I saw in the source code that Spring DATA Rest can expose a Json Schema for a repository with this URL : /{repository}/schema.
Is there anybody who know how to configure this ?
There is the RepositorySchemaController (org.springframework.data.rest.webmvc) but i have not found how to use it.
version : 2.0.0.M1
Make sure you set the right headers...
Request - /{repository}/schema
Header - Accept: application/json+schema
Also if you haven't looked into 2.0 snapshots, there are lot more features and changes coming up
EDIT: Jan 27 2014
Correction:
Accept should be "application/schema+json" instead of "application/json+schema"
Request - /{repository}/schema
Header - Accept: application/schema+json
Just an update for version 2.4.0 of Spring Data REST:
JSONSchema is now exposed under the profile link, so you need to change your request as follows:
Request: /profile/{repository}
Header: Accept: application/schema+json
You can find the updated documentation here.
This actually returns the ALPS style representation. If you are interested in other formats, e.g. JSON Schema, you might want to add your own controller to be flexible:
#BasePathAwareController
public class JsonSchemaController {
public static final String PROFILE_ROOT_MAPPING = "/schema";
public static final String RESOURCE_PROFILE_MAPPING = PROFILE_ROOT_MAPPING + "/{repository}";
#RequestMapping(value = RESOURCE_PROFILE_MAPPING, method = GET)
public ResponseEntity<JsonNode> descriptor(RootResourceInformation information) {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonSchema = generator.generateSchema(information.getPersistentEntity().getType());
return ResponseEntity.ok(jsonSchema);
}
}
Dependencies for Snow:
implementation 'com.github.victools:jsonschema-generator:4.16.0'