How to add a header to a Vapor response (Cache-Control) - vapor

I have a controller using a get handler that returns a Future<Content>. I would like to add a header to the response (Cache-Control to be specific). I was thinking that it should be easy but I'm not finding how to do it. Which would be the way to add a header in this case? When we are working with Content instead of Response

To solve the problem you could write your endpoint like this
struct Something: Content {
let text: String
}
router.get("customresponse") { req -> Future<Response> in
return try Something(text: "Hello world").encode(for: req).map { response in
response.http.headers.add(name: .cacheControl, value: "something")
return response
}
}

Mike's answer is spot on... This worked for me. As a further reference, I used a value to extend the cache for 1 day on all public caches.
req.headers.add(name: .cacheControl, value: "public, max-age=86400")

Related

Webflux statuscode can not be changed

So I have one AuthenticationWebFilter to add a trigger when someone is Authenticated like this:
val builder : HttpSecurity.AuthorizeExchangeBuilder = http
.addFilterAt(
CustomAuthenticationWebFilter(securityContextRepository),
SecurityWebFiltersOrder.AUTHENTICATION
)
.authorizeExchange().permitAll()
And then in "CustomAuthenticationWebFilter" I change the statusCode with some logic, somethig like this:
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
return super.filter(exchange, chain)
.doOnSuccess {
exchange.response.statusCode = UNAUTHORIZED
return#doOnSuccess
}
The point is, when I am doing a request, the flow is going to before to the WebFilter
AbstractServerHttpResponse#doCommit
and then the state is change to "COMMITTED" and when I call to
AbstractServerHttpResponse#setStatusCode
it's not possible to change it.
It's possible to change the statusCode before be commited?
build.gradle
compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.security:spring-security-web')
compile('org.springframework.security:spring-security-config')
Ok, after testing so many things this days, I resolved it just changing the "SecurityWebFiltersOrder" to "SecurityWebFiltersOrder.AUTHORIZATION"

Handling re-authentication with RxSwift and Moya

I have a project where I'm using Moya with RxSwift extensions.
The simple use cases work fine, I'm able to do requests and get responses in the form of Observables.
public func test() -> Observable<Response> {
return provider
.request(.test)
.retry(5)
}
I can then subscribe to the observable and print the response with no problem.
But now I need to handle authentication logic. The way it works is that I run the above request with a token added as an HTTP Header Field. Moya allows me to that transparently by using endpointByAddingHTTPHeaderFields in the endpointClosure. No problem so far.
The problem arises when the request fails with HTTP status 401, that means I need to re-authenticate by calling another endpoint
provider.request(.auth(user, pass)).retry(5)
This returns another Observable that I can easily map to JSON to get the new token.
I then just have to call test() again!
So my question is... How can I add this authentication logic inside the test() function above, so that the Observable returned by test() is already guaranteed to have run the re-authentication logic in case of failure and be the result of a second re-authenticated request.
I'm very new to RXSwift and RX in general, so I'm a bit clueless about the operators I would use to do this.
Thanks!
public func test(with authToken: String) -> Observable<Response> {
return provider
.request(.test)
.endpointByAddingHTTPHeaderFields(["Authorization": authToken])
.catchError { error in
if needsReauth(error) {
return provider.request(.auth(user, pass)).map { parseToken($0) }
.flatMap { token in
return test(with: token)
}
} else {
return .error(error)
}
}
}
catchError enables to continue observable's execution using another observable. The observable we define here does the following:
First, it will request the .auth endpoint.
It then reads from the response to get the new auth token
Finally, we recursively call test(with authToken: String) to retry querying the test enpoint.

Dart Date String Formatting [duplicate]

Is there a function to do urlencoding in Dart? I am doing a AJAX call using XMLHttpRequest object and I need the url to be url encoded.
I did a search on dartlang.org, but it didn't turn up any results.
var uri = 'http://example.org/api?foo=some message';
var encoded = Uri.encodeFull(uri);
assert(encoded == 'http://example.org/api?foo=some%20message');
var decoded = Uri.decodeFull(encoded);
assert(uri == decoded);
http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#ch03-uri
Update: There is now support for encode/decode URI in the Dart Uri class
Dart's URI code is placed in a separate library called dart:uri (so it can be shared between both dart:html and dart:io). It looks like it currently does not include a urlencode function so your best alternative, for now, is probably to use this Dart implementation of JavaScript's encodeUriComponent.
Uri.encodeComponent(url); // To encode url
Uri.decodeComponent(encodedUrl); // To decode url
I wrote this small function to convert a Map into a URL encoded string, which may be what you're looking for.
String encodeMap(Map data) {
return data.keys.map((key) => "${Uri.encodeComponent(key)}=${Uri.encodeComponent(data[key])}").join("&");
}
I dont' think there is yet. Check out http://unpythonic.blogspot.com/2011/11/oauth20-and-jsonp-with-dartin-web.html and the encodeComponent method.
Note, it's lacking some characters too, it needs to be expanded. Dart really should have this built in and easy to get to. It may have it in fact, but I didn't find it.
Safe Url Encoding in flutter
Ex.
String url = 'http://example.org/';
String postDataKey = "requestParam="
String postData = 'hdfhghdf+fdfbjdfjjndf'
In Case of get request :
Uri.encodeComponent(url+postDataKey+postData);
In Case of Post Data Request use flutter_inappwebview library
var data = postDataKey + Uri.encodeComponent(postData);
webViewController.postUrl(url: Uri.parse(url), postData: utf8.encode(data));
Uri.encodeComponent() is correct, Uri.encodeFull() has a bug, see below example:
void main() {
print('$text\n');
var coded = Uri.encodeFull(text);
print(coded);
print('\n');
coded = Uri.encodeComponent(text);
print(coded);
}
var text = '#2020-02-29T142022Z_1523651918_RC2EAF9OOHDB_RT.jpg';

Json isn't returning a result that is accessible as an array

I'm having troubles reading a Json result back from a controller method...
I have this method in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GetCurrent()
{
IList<string> profile = new List<string>();
profile.Add("-1");
profile.Add("Test");
profile.Add("");
return this.Json(profile);
}
And it is being called by this jquery ajax post:
$.post("/Profile/GetCurrent", function(profile) { profileCompleteOpen(profile); }, "json");
and the javascript function called on the post's callback:
function profileCompleteOpen(profile) {
alert(profile);
alert(profile[0]);
}
The result of the first alert shows the array like this:
["-1","Test",""]
But the result of the second alert shows this:
[
rather than
-1
What am I doing wrong here... I've compared it to one of the other times I'm doing this and it seems to be the exact same. Why isn't it recognizing it's an array?
Thanks,
Matt
Try converting the json data in profile to a proper object by using eval() on it.
Example:
var profileObject = eval('(' + profile + ')');
Hmmm, I'd be doing what you're trying to do a little differently.
I'd either return a fully qualified object and then use it's properties;
class MyObj
{
public string name{get;set;}
}
fill the object and return it as a json object. then you're jquery code can access like any other object.
The other way might be to do a return PartialView("MyView", model);
That will return the partial view as html back to your page which you can then append to your html.
I think the type of profile is string instead of array. Why? Check the $.post method parameters. Maybe the problem is there.
$.post("url", null, function(profile) { ... }, "json");

ASP.NET MVC How to pass JSON object from View to Controller as Parameter

I have a complex JSON object which is sent to the View without any issues (as shown below) but I cannot work out how Serialize this data back to a .NET object when it is passed back to the controller through an AJAX call. Details of the various parts are below.
var ObjectA = {
"Name": 1,
"Starting": new Date(1221644506800),
"Timeline": [
{
"StartTime": new Date(1221644506800),
"GoesFor": 200
}
,
{
"StartTime": new Date(1221644506800),
"GoesFor": 100
}
]
};
I am not sure how this object can be passed to a Controller Method, I have this method below where the Timelines object mirrors the above JS object using Properties.
public JsonResult Save(Timelines person)
The jQuery I am using is:
var encoded = $.toJSON(SessionSchedule);
$.ajax({
url: "/Timeline/Save",
type: "POST",
dataType: 'json',
data: encoded,
contentType: "application/json; charset=utf-8",
beforeSend: function() { $("#saveStatus").html("Saving").show(); },
success: function(result) {
alert(result.Result);
$("#saveStatus").html(result.Result).show();
}
});
I have seen this question which is similar, but not quite the same as I am not using a forms to manipulate the data.
How to pass complex type using json to ASP.NET MVC controller
I have also seen references to using a 'JsonFilter' to manually deserialize the JSON, but was wondering if there is a way to do it nativly though ASP.NET MVC? Or what are the best practices for passing data in this way?
Edit:
This method should no longer be needed with the arrival of MVC 3, as it will be handled automatically - http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
You can use this ObjectFilter:
public class ObjectFilter : ActionFilterAttribute {
public string Param { get; set; }
public Type RootType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if ((filterContext.HttpContext.Request.ContentType ?? string.Empty).Contains("application/json")) {
object o =
new DataContractJsonSerializer(RootType).ReadObject(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters[Param] = o;
}
}
}
You can then apply it to your controller methods like so:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
So basically, if the content type of the post is "application/json" this will spring into action and will map the values to the object of type you specify.
You say "I am not using a forms to manipulate the data." But you are doing a POST. Therefore, you are, in fact, using a form, even if it's empty.
$.ajax's dataType tells jQuery what type the server will return, not what you are passing. POST can only pass a form. jQuery will convert data to key/value pairs and pass it as a query string. From the docs:
Data to be sent to the server. It is
converted to a query string, if not
already a string. It's appended to the
url for GET-requests. See processData
option to prevent this automatic
processing. Object must be Key/Value
pairs. If value is an Array, jQuery
serializes multiple values with same
key i.e. {foo:["bar1", "bar2"]}
becomes '&foo=bar1&foo=bar2'.
Therefore:
You aren't passing JSON to the server. You're passing JSON to jQuery.
Model binding happens in the same way it happens in any other case.
A different take with a simple jQuery plugin
Even though answers to this question are long overdue, but I'm still posting a nice solution that I came with some time ago and makes it really simple to send complex JSON to Asp.net MVC controller actions so they are model bound to whatever strong type parameters.
This plugin supports dates just as well, so they get converted to their DateTime counterpart without a problem.
You can find all the details in my blog post where I examine the problem and provide code necessary to accomplish this.
All you have to do is to use this plugin on the client side. An Ajax request would look like this:
$.ajax({
type: "POST",
url: "SomeURL",
data: $.toDictionary(yourComplexJSONobject),
success: function() { ... },
error: function() { ... }
});
But this is just part of the whole problem. Now we are able to post complex JSON back to server, but since it will be model bound to a complex type that may have validation attributes on properties things may fail at that point. I've got a solution for it as well. My solution takes advantage of jQuery Ajax functionality where results can be successful or erroneous (just as shown in the upper code). So when validation would fail, error function would get called as it's supposed to be.
There is the JavaScriptSerializer class you can use too. That will let you deserialize the json to a .NET object. There's a generic Deserialize<T>, though you will need the .NET object to have a similar signature as the javascript one. Additionally there is also a DeserializeObject method that just makes a plain object. You can then use reflection to get at the properties you need.
If your controller takes a FormCollection, and you didn't add anything else to the data the json should be in form[0]:
public ActionResult Save(FormCollection forms) {
string json = forms[0];
// do your thing here.
}
This answer is a follow up to DaRKoN_'s answer that utilized the object filter:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
I was having a problem figuring out how to send multiple parameters to an action method and have one of them be the json object and the other be a plain string. I'm new to MVC and I had just forgotten that I already solved this problem with non-ajaxed views.
What I would do if I needed, say, two different objects on a view. I would create a ViewModel class. So say I needed the person object and the address object, I would do the following:
public class SomeViewModel()
{
public Person Person { get; set; }
public Address Address { get; set; }
}
Then I would bind the view to SomeViewModel. You can do the same thing with JSON.
[ObjectFilter(Param = "jsonViewModel", RootType = typeof(JsonViewModel))] // Don't forget to add the object filter class in DaRKoN_'s answer.
public JsonResult doJsonStuff(JsonViewModel jsonViewModel)
{
Person p = jsonViewModel.Person;
Address a = jsonViewModel.Address;
// Do stuff
jsonViewModel.Person = p;
jsonViewModel.Address = a;
return Json(jsonViewModel);
}
Then in the view you can use a simple call with JQuery like this:
var json = {
Person: { Name: "John Doe", Sex: "Male", Age: 23 },
Address: { Street: "123 fk st.", City: "Redmond", State: "Washington" }
};
$.ajax({
url: 'home/doJsonStuff',
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(json), //You'll need to reference json2.js
success: function (response)
{
var person = response.Person;
var address = response.Address;
}
});
in response to Dan's comment above:
I am using this method to implement
the same thing, but for some reason I
am getting an exception on the
ReadObject method: "Expecting element
'root' from namespace ''.. Encountered
'None' with name '', namespace ''."
Any ideas why? – Dan Appleyard Apr 6
'10 at 17:57
I had the same problem (MVC 3 build 3.0.11209.0), and the post below solved it for me. Basically the json serializer is trying to read a stream which is not at the beginning, so repositioning the stream to 0 'fixed' it...
http://nali.org/asp-net-mvc-expecting-element-root-from-namespace-encountered-none-with-name-namespace/

Resources