Will HttpGet headers override httpClient headers (Apache HttpClient)? - httpclient

I want to set header at the HttpClient, since I want most of my service calls to have the default headers.
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(ApacheHttpClient.DEFAULT_CONNECTION_TIMEOUT)
.setSocketTimeout(ApacheHttpClient.DEFAULT_SOCKET_TIMEOUT).build();
httpClient = HttpClients.custom().setDefaultHeaders(getDefaultHeaderList()).setDefaultRequestConfig(requestConfig).setRetryHandler(new RetryHandler()).build();
However, I would like to replace the default headers, if required. I would be setting it as HttpGet header.
int cTimeout = (connTimeout == null) ? DEFAULT_CONNECTION_TIMEOUT : connTimeout;
int sTimeout = (socketTimeout == null) ? DEFAULT_SOCKET_TIMEOUT : socketTimeout;
httpGet.setConfig(RequestConfig.custom().setConnectTimeout(cTimeout).setSocketTimeout(sTimeout).build());
Would the HttpGet header override the headers set at the HttpClient?

Debugging the request, in org.apache.http.impl.client.InternalHttpClient.doExecute(HttpHost, HttpRequest, HttpContext) I can see that config which holds the timeouts is first obtained from the request and if it not set, the default is used:
RequestConfig config = null;
if (request instanceof Configurable) {
config = ((Configurable) request).getConfig();
}
if (config == null) {
final HttpParams params = request.getParams();
if (params instanceof HttpParamsNames) {
if (!((HttpParamsNames) params).getNames().isEmpty()) {
config = HttpClientParamConfig.getRequestConfig(params);
}
} else {
config = HttpClientParamConfig.getRequestConfig(params);
}
}
if (config != null) {
localcontext.setRequestConfig(config);
}
setupContext(localcontext);

Related

Redirect asp.net core 2.0 urls to lowercase

I have seen that you can configure routing in ASP.NET Core 2.0 to generate lower case urls as described here: https://stackoverflow.com/a/45777372/83825
Using this:
services.AddRouting(options => options.LowercaseUrls = true);
However, although this is fine to GENERATE the urls, it doesn't appear to do anything to actually ENFORCE them, that is, redirect any urls that are NOT all lowercase to the corresponding lowercase url (preferably via 301 redirect).
I know people are accessing my site via differently cased urls, and I want them to all be lowercase, permanently.
Is doing a standard redirect via RewriteOptions and Regex the only way to do this? what would be the appropriate expression to do this:
var options = new RewriteOptions().AddRedirect("???", "????");
Or is there another way?
I appreciate this is many months old, however for people who may be looking for the same solution, you can add a complex redirect implementing IRule such as:
public class RedirectLowerCaseRule : IRule
{
public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
PathString path = context.HttpContext.Request.Path;
HostString host = context.HttpContext.Request.Host;
if (path.HasValue && path.Value.Any(char.IsUpper) || host.HasValue && host.Value.Any(char.IsUpper))
{
HttpResponse response = context.HttpContext.Response;
response.StatusCode = StatusCode;
response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase.Value + request.Path.Value).ToLower() + request.QueryString;
context.Result = RuleResult.EndResponse;
}
else
{
context.Result = RuleResult.ContinueRules;
}
}
}
This can then be applied in your Startup.cs under Configure method as such:
new RewriteOptions().Add(new RedirectLowerCaseRule());
Slightly different implementation, also inspired from this other thread.
public class RedirectLowerCaseRule : IRule
{
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
string url = request.Scheme + "://" + request.Host + request.PathBase + request.Path;
bool isGet = request.Method.ToLowerInvariant().Contains("get");
if ( isGet && url.Contains(".") == false && Regex.IsMatch(url, #"[A-Z]") )
{
HttpResponse response = context.HttpContext.Response;
response.Clear();
response.StatusCode = StatusCodes.Status301MovedPermanently;
response.Headers[HeaderNames.Location] = url.ToLowerInvariant() + request.QueryString;
context.Result = RuleResult.EndResponse;
}
else
{
context.Result = RuleResult.ContinueRules;
}
}
}
Changes made that I find useful:
Using ToLowerInvariant() instead of ToLower() (see possible issues here)
Keeping the port number in place.
Bypassing request methods other than GET.
Bypassing requests with a dot, assuming static files like js/css/images etc should keep any uppercase in place.
Using Microsoft.AspNetCore.Http.StatusCodes.
Adding as answer because I can't comment (yet). This is an addition to Ben Maxfields answer.
Using his code http://www.example.org/Example/example would NOT be redirected, since PathBase was not checked for uppercase letters (even though it was used to build the new lowercase URI).
So based on his code, I ended up using this:
public class RedirectLowerCaseRule : IRule
{
public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
PathString path = context.HttpContext.Request.Path;
PathString pathbase = context.HttpContext.Request.PathBase;
HostString host = context.HttpContext.Request.Host;
if ((path.HasValue && path.Value.Any(char.IsUpper)) || (host.HasValue && host.Value.Any(char.IsUpper)) || (pathbase.HasValue && pathbase.Value.Any(char.IsUpper)))
{
Console.WriteLine("Redirect should happen");
HttpResponse response = context.HttpContext.Response;
response.StatusCode = StatusCode;
response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString;
context.Result = RuleResult.EndResponse;
}
else
{
context.Result = RuleResult.ContinueRules;
}
}
}
My two cents... based on https://github.com/aspnet/AspNetCore/blob/master/src/Middleware/Rewrite/src/RedirectToWwwRule.cs
public class RedirectToLowercaseRule : IRule
{
private readonly int _statusCode;
public RedirectToLowercaseRule(int statusCode)
{
_statusCode = statusCode;
}
public void ApplyRule(RewriteContext context)
{
var req = context.HttpContext.Request;
if (!req.Scheme.Any(char.IsUpper)
&& !req.Host.Value.Any(char.IsUpper)
&& !req.PathBase.Value.Any(char.IsUpper)
&& !req.Path.Value.Any(char.IsUpper))
{
context.Result = RuleResult.ContinueRules;
return;
}
var newUrl = UriHelper.BuildAbsolute(req.Scheme.ToLowerInvariant(), new HostString(req.Host.Value.ToLowerInvariant()), req.PathBase.Value.ToLowerInvariant(), req.Path.Value.ToLowerInvariant(), req.QueryString);
var response = context.HttpContext.Response;
response.StatusCode = _statusCode;
response.Headers[HeaderNames.Location] = newUrl;
context.Result = RuleResult.EndResponse;
context.Logger.RedirectedToLowercase();
}
}
With extension methods:
public static class RewriteOptionsExtensions
{
public static RewriteOptions AddRedirectToLowercase(this RewriteOptions options, int statusCode)
{
options.Add(new RedirectToLowercaseRule(statusCode));
return options;
}
public static RewriteOptions AddRedirectToLowercase(this RewriteOptions options)
{
return AddRedirectToLowercase(options, StatusCodes.Status307TemporaryRedirect);
}
public static RewriteOptions AddRedirectToLowercasePermanent(this RewriteOptions options)
{
return AddRedirectToLowercase(options, StatusCodes.Status308PermanentRedirect);
}
}
And logging:
internal static class MiddlewareLoggingExtensions
{
private static readonly Action<ILogger, Exception> _redirectedToLowercase = LoggerMessage.Define(LogLevel.Information, new EventId(1, "RedirectedToLowercase"), "Request redirected to lowercase");
public static void RedirectedToLowercase(this ILogger logger)
{
_redirectedToLowercase(logger, null);
}
}
And usage:
app.UseRewriter(new RewriteOptions()
.AddRedirectToLowercase());
Other considerations are the choice of status codes. I've used 307 and 308 in the extension methods as these prevent the request method being changed (e.g. from GET to POST) during the request, however if you want to allow that behaviour you can use 301 and 302. See What's the difference between HTTP 301 and 308 status codes? for further information.
Are you sure you want a redirect? If not, and your goal is that there is no such thing as uppercase in your host and path, you can use the following IRule. This assures me that wherever I look at the path in the pipeline that it is lowercase.
public class RewriteLowerCaseRule : IRule
{
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
var host = request.Host;
var pathBase = request.PathBase;
var path = request.Path;
if (host.HasValue)
{
if (host.Port == null)
{
request.Host = new HostString(host.Host.ToLower());
}
else
{
request.Host = new HostString(host.Host.ToLower(), (int) host.Port);
}
}
if (pathBase.HasValue)
{
request.PathBase = new PathString(pathBase.Value.ToLower());
}
if (path.HasValue)
{
request.Path = new PathString(path.Value.ToLower());
request.PathBase = new PathString(pathBase.Value.ToLower());
}
context.Result = RuleResult.ContinueRules;
}
}
Usage:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseRewriter(new RewriteOptions().Add(new RewriteLowerCaseRule()));
...
}

Using HTTPClientParams for HttpClient 4.5

I have a code with HttpClient 3.x which uses HTTPClientParams.
HttpClientParams params = new HttpClientParams();
params.setVersion(HttpVersion.HTTP_1_1);
params.setContentCharset(ENCODING);
try {
URI uri = new URI(some-resource);
int port = uri.getPort();
Protocol protocol = null;
if(port == -1){
if(uri.getScheme().compareToIgnoreCase("http") == 0){
port = 80;
protocol = Protocol.getProtocol("http");
}
else if(uri.getScheme().compareToIgnoreCase("https") == 0){
port = 443;
protocol = Protocol.getProtocol("https");
}
}
Protocol.registerProtocol(uri.getScheme(), protocol);
HttpConnectionManager manager = new SimpleHttpConnectionManager();
HttpClient client = new HttpClient(manager);
client.setParams(params);
I have verified that for HTTPClient 4.5 there are not HTTPParam method. How can I upgrade the same? Are there any alternatives?
Please have a look at RequestConfig class
This code should be roughly equivalent to your code above
CloseableHttpClient client = HttpClientBuilder.create()
.setConnectionManager(new BasicHttpClientConnectionManager())
.setDefaultRequestConfig(RequestConfig
.custom()
// Add custom request parameters
.build())
.build();
URI uri = new URI(some - resource);
HttpGet httpGet = new HttpGet(uri);
httpGet.setProtocolVersion(HttpVersion.HTTP_1_1);
try (CloseableHttpResponse response1 = client.execute(httpGet)) {
EntityUtils.toString(response1.getEntity(), Charset.forName(ENCODING));
}

Angular file upload with rest easy jax rs java ee web service

I am trying to upload an image to a server using Angular as the front end and java ee web service jax rs rest easy as my back end. This my code for the angular/front end:
HTML page:
<md-card>
<input type="file" (change)="onChange($event)" placeholder="Upload files" >
</md-card>
For the component:
fileChange(event) {
let fileList: FileList = event.target.files;
let fileListLength = fileList.length;
if(fileListLength > 0) {
let formData:FormData = new FormData();
for (var i = 0; i < fileListLength; i++) {
formData.append("uploadFile[]", fileList[i]);
}
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
this.http.post("http://localhost:8080/BCWEB/uploadProdImage", formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
)
}
}
For the backend java ee restful web service:
private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/007EAA/Downloads/tes/";
#POST
#Path("/uploadProdImage")
#Consumes("multipart/form-data")
public Response uploadFile2(MultipartFormDataInput input) {
String fileName = "";
Map<String, List<InputPart>> formParts = input.getFormDataMap();
List<InputPart> inPart = formParts.get("file");
for (InputPart inputPart : inPart) {
try {
// Retrieve headers, read the Content-Disposition header to obtain the original name of the file
MultivaluedMap<String, String> headers = inputPart.getHeaders();
fileName = parseFileName(headers);
// Handle the body of that part with an InputStream
InputStream istream = inputPart.getBody(InputStream.class,null);
fileName = SERVER_UPLOAD_LOCATION_FOLDER + fileName;
saveFile(istream,fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
String output = "File saved to server location : " + fileName;
return Response.status(200).entity(output).build();
}
// Parse Content-Disposition header to get the original file name
private String parseFileName(MultivaluedMap<String, String> headers) {
String[] contentDispositionHeader = headers.getFirst("Content-Disposition").split(";");
for (String name : contentDispositionHeader) {
if ((name.trim().startsWith("filename"))) {
String[] tmp = name.split("=");
String fileName = tmp[1].trim().replaceAll("\"","");
return fileName;
}
}
return "randomName";
}
// save uploaded file to a defined location on the server
private void saveFile(InputStream uploadedInputStream,
String serverLocation) {
try {
OutputStream outpuStream = new FileOutputStream(new File(serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
outpuStream.write(bytes, 0, read);
}
outpuStream.flush();
outpuStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
The problem is No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had an HTTP status code of 500.
But when I try to add a user on the server, it gets added without any problem, so it's not a server problem. Can anyone help?
The issue is that your frontend and backend exist across different local hosts, and by default cross-origin requests are denied for security reasons.
You'll want to enable cross origin requests on your backend during testing, and disable it for production when your frontend and backend live in the same area. To enable cross origin, you'll need to add this provider snippet:
package com.yourdomain.package;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext cres) throws IOException {
cres.getHeaders().add("Access-Control-Allow-Origin", "*");
cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
cres.getHeaders().add("Access-Control-Max-Age", "1209600");
}
}
Info on CORS
Snippet pulled from this SO question

Include default query param values in requestURI in Jersey if they are omitted

Is there a way to have query parameter "x" be included in the URL with the default value if it is omitted, because currently what I need to do is the following below and I have to do that for each parameter with a default value; is there a better way to do this? I am using Jersey version 1.12
public Response getResponse(#DefaultValue("test") #QueryParam("x") String x)
UriBuilder uriBuilder = UriBuilder.fromUri(uriInfo.getRequestUri());
uriBuilder = uriBuilder.replaceQueryParam(x);
QueryContext.setUrl(uriBuilder.build().toString());
Thank you
I'm not aware of a standard Jersey way to do this but you could intercept the method with AOP and do something like:
public class UpdateQueryParamInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation method) throws Throwable {
UriBuilder builder = null;
for (Object arg: method.getArguments()) {
if (arg instanceof UriInfo) {
UriInfo info = (UriInfo)arg;
builder = UriBuilder.fromUri(info.getRequestUri());
}
}
Annotation[][] annotations = method.getMethod().getParameterAnnotations();
for (int i = 0; i < method.getArguments().length; i++) {
if (annotations[i].length > 0) {
DefaultValue def = null;
QueryParam param = null;
for (Annotation annotation: annotations[i]) {
if (annotation instanceof DefaultValue) {
def = (DefaultValue)annotation;
} else if (annotation instanceof QueryParam) {
param = (QueryParam)annotation;
}
}
if ((null != def) && (null != param)) {
builder = builder.replaceQueryParam(param.value(), method.getArguments()[i]);
}
}
}
//Do something with builder.build().toString());
return method.proceed();
}
}
I wasn't sure what QueryContext class was in your code - but you could find that in the method also and perform the setUrl method in the interceptor...

ASP.NET MVC TempData in browser cookie

I am trying to use a custom ITempDataProvider provider to store TempData in a browser's cookie instead of session state. However, everything works fine except that I am unable to remove the cookie from the Response stream after reading it.
Any ideas?
Thanks!
public class CookieTempDataProvider : ITempDataProvider
{
internal const string TempDataCookieKey = "__ControllerTempData";
HttpContextBase _httpContext;
public CookieTempDataProvider(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
_httpContext = httpContext;
}
public HttpContextBase HttpContext
{
get
{
return _httpContext;
}
}
protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
{
IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value);
// Remove cookie
cookie.Expires = DateTime.MinValue;
cookie.Value = string.Empty;
_httpContext.Request.Cookies.Remove(TempDataCookieKey);
if (_httpContext.Response != null && _httpContext.Response.Cookies != null)
{
HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey];
if (responseCookie != null)
{
// Remove cookie
cookie.Expires = DateTime.MinValue;
cookie.Value = string.Empty;
_httpContext.Response.Cookies.Remove(TempDataCookieKey);
}
}
return deserializedTempData;
}
return new Dictionary<string, object>();
}
protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
string cookieValue = SerializeToBase64EncodedString(values);
var cookie = new HttpCookie(TempDataCookieKey);
cookie.HttpOnly = true;
cookie.Value = cookieValue;
_httpContext.Response.Cookies.Add(cookie);
}
public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)
{
byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
var memStream = new MemoryStream(bytes);
var binFormatter = new BinaryFormatter();
return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/;
}
public static string SerializeToBase64EncodedString(IDictionary<string, object> values)
{
MemoryStream memStream = new MemoryStream();
memStream.Seek(0, SeekOrigin.Begin);
var binFormatter = new BinaryFormatter();
binFormatter.Serialize(memStream, values);
memStream.Seek(0, SeekOrigin.Begin);
byte[] bytes = memStream.ToArray();
return Convert.ToBase64String(bytes);
}
IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext)
{
return LoadTempData(controllerContext);
}
void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
SaveTempData(controllerContext, values);
}
}
There is a better solution by Brock Allen on GitHub that uses encryption, 2 forms of serialization, and compression to protect and optimize the cookies.
https://github.com/brockallen/CookieTempData
Here is a link to the blog about it:
http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/
He also has a good technique using IControllerFactory to ensure every controller is supplied with an instance of ITempDataProvider.
Hi I too had the same issue and it was an issue with the implementation of CookieTempDataProvider.
So I modified the code a bit and now it works perfectly.
When it reads the data from the cookie, it removes it from both the request and response. But add another cookie with an empty value in the SaveData function which is called when the request processing is completed.
Points to note : If you want to remove a cookie, you have to set the timeout value and send it back to the client and then the browser will remove it. We cannot do it otherwise from the code a the cookie is handled by the browser
And I found out that setting the expiration to DateTime.MinValue does not expire the cookie in chrome (don't know about the other browsers) so I set it to 2001-01-01 :)
Here is the working code
public class CookieTempDataProvider : ITempDataProvider
{
internal const string TempDataCookieKey = "__ControllerTempData";
HttpContextBase _httpContext;
public CookieTempDataProvider(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
_httpContext = httpContext;
}
public HttpContextBase HttpContext
{
get
{
return _httpContext;
}
}
protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey)) //we need this because
//Cookies[TempDataCookieKey] will create the cookie if it does not exist
{
HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
{
IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value);
// Remove cookie
cookie.Expires = new DateTime(2000, 1, 1);
cookie.Value = string.Empty;
_httpContext.Request.Cookies.Remove(TempDataCookieKey);
if (_httpContext.Response != null && _httpContext.Response.Cookies != null)
{
HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey];
if (responseCookie != null)
{
// Remove cookie
cookie.Expires = new DateTime(2000, 1, 1);
cookie.Value = string.Empty;
_httpContext.Response.Cookies.Remove(TempDataCookieKey);
}
}
return deserializedTempData;
}
}
return new Dictionary<string, object>();
}
protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
if (values != null && values.Count > 0)
{
//there are values to set, so add the cookie. But no need to expire it as we need the browser to send the
//cookie back with the next request
string cookieValue = SerializeToBase64EncodedString(values);
var cookie = new HttpCookie(TempDataCookieKey);
cookie.HttpOnly = true;
cookie.Value = cookieValue;
_httpContext.Response.Cookies.Add(cookie);
}
else
{
//Still we need to add the cookie with the expiration set, to make the client browser remove the cookie from the request.
//Otherwise the browser will continue to send the cookie with the response
//Also we need to do this only if the requet had a tempdata cookie
if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey))
{
{
HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
// Remove the request cookie
cookie.Expires = new DateTime(2000, 1, 1);
cookie.Value = string.Empty;
_httpContext.Request.Cookies.Remove(TempDataCookieKey);
var rescookie = new HttpCookie(TempDataCookieKey);
rescookie.HttpOnly = true;
rescookie.Value = "";
rescookie.Expires = new DateTime(2000, 1, 1); //so that the browser will remove the cookie when it receives the request
_httpContext.Response.Cookies.Add(rescookie);
}
}
}
}
public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)
{
byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
var memStream = new MemoryStream(bytes);
var binFormatter = new BinaryFormatter();
return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/;
}
public static string SerializeToBase64EncodedString(IDictionary<string, object> values)
{
MemoryStream memStream = new MemoryStream();
memStream.Seek(0, SeekOrigin.Begin);
var binFormatter = new BinaryFormatter();
binFormatter.Serialize(memStream, values);
memStream.Seek(0, SeekOrigin.Begin);
byte[] bytes = memStream.ToArray();
return Convert.ToBase64String(bytes);
}
IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext)
{
return LoadTempData(controllerContext);
}
void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
SaveTempData(controllerContext, values);
}
}
Here is an example of a working solution without lots of excess code. It uses Json.NET for serializing, which is faster than BinaryFormatter + Base64Encoding and also produces a much shorter string (=less http overhead).
public class CookieTempDataProvider : ITempDataProvider
{
const string cookieKey = "temp";
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
var cookie = controllerContext.HttpContext.Request.Cookies[cookieKey];
if (cookie != null) {
return JsonConvert.DeserializeObject<IDictionary<string, object>>(cookie.Value);
}
return null;
}
// Method is called after action execution. The dictionary mirrors the contents of TempData.
// If there are any values in the dictionary, save it in a cookie. If the dictionary is empty,
// remove the cookie if it exists.
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
var ctx = controllerContext.HttpContext;
if (values.Count > 0) {
var cookie = new HttpCookie(cookieKey)
{
HttpOnly = true,
Value = JsonConvert.SerializeObject(values)
};
ctx.Response.Cookies.Add(cookie);
} else if (ctx.Request.Cookies[cookieKey] != null) {
// Expire cookie to remove it from browser.
ctx.Response.Cookies[cookieKey].Expires = DateTime.Today.AddDays(-1);
}
}
}