I am using freemarker template engine with Spring and I want to add or replace lang=xxx parameter in the href attribute of link element when rendering page.
The closest solution I have found is following:
<a href="${springMacroRequestContext.getRequestUri()}?lang=en'/>English</a>
But this is not sufficient when I have URL with parameters and fragments because I will miss them. For example http://localhost/sports/search?query=Volleyball&lang=cs#some-fragment results in http://localhost/sports/search?lang=en
How to compose the URL with added or changed lang parameter in freemarker and do not miss any part of requested URL?
I preferred to do it in Java, this simple implementation does not take into account hashes (#) or the presence of the given parameter in the url..
public static String addParamToUrl(String url, String paramName, String paramValue){
StringBuffer buffer = new StringBuffer(url);
//adding separator
if(url.indexOf('?') == -1){
buffer.append('?');
}else if(!url.endsWith("?") && !url.endsWith("&")){
buffer.append('&');
}
buffer.append(paramName);
if(paramValue != null){
buffer.append("=");
buffer.append(URLEncoder.encode(paramValue, "UTF-8"));
}
return buffer.toString();
}
Put this method in a Class (i.e:Utils.java) that can be staticly accessed by Freemarker engine and than:
<#assign url = Utils.addParamToUrl(springMacroRequestContext.getRequestUri(), "lang", "en") />
English
To expose your Utils class you have to customize FreemarkerManager
public class MyFreemarkerManager extends FreemarkerManager {
public MyFreemarkerManager(){
super();
}
#Override
protected void populateContext(ScopesHashModel model, ValueStack stack, Object action, HttpServletRequest request, HttpServletResponse response) {
super.populateContext(model, stack, action, request, response);
try {
BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_2_3_24).build();
TemplateHashModel staticModels = beansWrapper.getStaticModels();
TemplateHashModel utils = (TemplateHashModel)staticModels.get("com.package.Utils");
model.put("Utils", utils);
} catch (TemplateModelException e) {
//...
}
}
}
Finally you have to tell Spring you are using MyFreemarkerManager as manager for Freemarker.
Related
I'm using RestTemplate to call an external resource using GET where the URI is https://external.com/resource/{resourceID}.
Accordingly, my RestTemplate calls looks like the following:
restTemplate.getForObject("https://external.com/resource/{resourceID}", String.class, uriMapObject)
Where uriMapObject is a map containing the ID variable.
I have also set a ClientHttpRequestInterceptor for my RestTemplate where the interceptor's function is to create a log item for that call and send it to ElasticSearch for logging.
#Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body, ClientHttpRequestExecution execution) throws IOException {
Map<String, Object> reqHeaders = new HashMap<>();
try{
// getPath() already contains the URI variable
String uri = httpRequest.getURI().getPath();
} catch (IllegalStateException e){
log.warn(e.getMessage());
}
.....
return response;
}
The issue is that within the ClientHttpRequestInterceptor method, I'm unable to access the original URI template used to derive the actual URL to call. I can only access the actual URL from the HttpRequest object which already has a unique identifier in it which in turn makes it impossible to aggregate all calls to https://external.com/resource/{resourceID} as a single pattern in Elastic.
Is there some way where I can get the URI template from within the interceptor?
URI template expansion is specified by the RestTemplate's UriTemplateHandler. You can customize the behavior of this by calling RestTemplate#setUriTemplateHandler, specifying your own implementation.
I would suggest creating a delegate object for a UriTemplateHandler, and wrapping what is set by default on the RestTemplate.
class LoggingUriTemplateHandler implements UriTemplateHandler {
private UriTemplateHandler delegate;
LoggingUriTemplateHandler(final UriTemplateHandler delegate) {
this.delegate = delegate;
}
public URI expand(final String template, final Map<String, ?> vars) {
//log template here
return this.delegate.expand(template, vars);
}
public URI expand(final String template, final Object... vars) {
//log template here
return this.delegate.expand(template, vars);
}
}
TL;DR - Is there a way to throw an error from a registered type converter during the MVC databinding phase such that it will return a response with a specific HTTP status code? I.e. if my converter can't find an object from the conversion source, can I return a 404?
I have a POJO:
public class Goofball {
private String id = "new";
// others
public String getName () { ... }
public void setName (String name) { ... }
}
and am using a StringToGoofballConverter to create an empty object when "new".equals(id) or try to load a Goofball from the database if it exists:
public Goofball convert(String idOrNew) {
Goofball result = null;
log.debug("Trying to convert " + idOrNew + " to Goofball");
if ("new".equalsIgnoreCase(idOrNew))
{
result = new Goofball ();
result.setId("new");
}
else
{
try
{
result = this.repository.findOne(idOrNew);
}
catch (Throwable ex)
{
log.error (ex);
}
if (result == null)
{
throw new GoofballNotFoundException(idOrNew);
}
}
return result;
}
That converter is used by spring when the request matches this endpoint:
#RequestMapping(value = "/admin/goofballs/{goofball}", method=RequestMethod.POST)
public String createOrEditGoofball (#ModelAttribute("goofball") #Valid Goofball object, BindingResult result, Model model) {
// ... handle the post and save the goofball if there were no binding errors, then return the template string name
}
This all works quite well insofar as GET requests to /admin/goofballs/new and /admin/goofballs/1234 work smoothly in the controller for both creating new objects and editing existing ones. The hitch is that if I issue a request with a bogus id, one that isn't new and also doesn't exist in the database I want to return a 404. Currently the Converter is throwing a custom exception:
#ResponseStatus(value= HttpStatus.NOT_FOUND, reason="Goofball Not Found") //404
public class GoofballNotFoundException extends RuntimeException {
private static final long serialVersionUID = 422445187706673678L;
public GoofballNotFoundException(String id){
super("GoofballNotFoundException with id=" + id);
}
}
but I started with a simple IllegalArgumentException as recommended in the Spring docs. In either case, the result is that Spring is returning a response with an HTTP status of 400.
This makes me think I'm misusing the Converter interface but that approach appears to be recommended by the #ModelAttribute docs.
So, again the question: is there a way to throw an error from a registered type converter during the databinding phase such that it will return a response with a specific HTTP status code?
Answering my own question:
Change StringToGoofballConverter to simply return null for the unfound entity instead of throwing IllegalArgumentException or a custom exception. The #Controller method will then be given a Goofball object that has a null id (e.g. the id is not "new" nor the path element value). At that point I can throw a GoofballNotFoundException or any other #ResponseStatus exception from there, within the controller method to affect the response status code.
I have implemented Spring XwsSecurityInterceptor and receiving soap message with <wsse:UsernameToken/> inside the <wsse:Security/> tag (OASIS WS-Security). It works fine.
Now I am trying to implement a logging interceptor to log the request/response soap messages in DB.
I can get the Security element in getSource() method of my custom logging interceptor (which extends org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor):
#Override
protected Source getSource(WebServiceMessage message) {
SaajSoapMessage soap = (SaajSoapMessage) message;
logger.info(Utils.getSoapEnvelopeAsString(soap));
// this envelop contains the <wsse:Security/> element as expected
// ...
// ...
}
But My problem is, when I extract the envelop inside my endpoint method, I don't get the <wsse:Security/> element in the header anymore.
public JAXBElement<MyResponseType> getRecepientInfo(#RequestPayload JAXBElement<MyRequestType> request, MessageContext messageContext) {
SaajSoapMessage soapReq = (SaajSoapMessage) messageContext.getRequest();
logger.info(Utils.getSoapEnvelope(soapReq));
// this envelop doesn't contain the <wsse:Security/> element
}
Here is the code for Utils.getSoapEnvelope(soap):
public static String getSoapEnvelope(SaajSoapMessage soapMessage) {
SoapEnvelope envelope = soapMessage.getEnvelope();
String envelopeMessge = "";
try {
envelopeMessge = Utils.getSourceAsString(envelope.getSource());
} catch (Exception e) {
// TODO handle Exception here.
}
return envelopeMessge;
}
public static String getSourceAsString(Source source) throws Exception{
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer xform = tfactory.newTransformer();
StringWriter writer = new StringWriter();
Result result = new StreamResult(writer);
xform.transform(source, result);
return writer.toString();
}
Does spring remove the <wsse:Security/> element from the header after authentication has been completed? Or, I am doing something wrong here?
How should I get the <wsse:Security/> element from header inside endpoint method?
I know this is a late answer but for whom it may interest I found out how to solve this.
You need to modify your securityPolicy.xml file so that the security header is kept. Simply set the attribute retainSecurityHeader to true. Here is an example of such a file:
<xwss:SecurityConfiguration retainSecurityHeader="true" dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:UsernameToken digestPassword="false" useNonce="false" id="someId"/>
</xwss:SecurityConfiguration>
I have found several sources that say that you should not use HttpContext.Current in WebApi but none that say how you should handle those cases where we used to use HttpContext.Current.
For example, I have a LinkProvider class that creates links for an object. (simplified to stay on topic).
public abstract class LinkProvider<T> : ILinkProvider<T>
{
protected ILink CreateLink(string linkRelation, string routeName, RouteValueDictionary routeValues)
{
var context = System.Web.HttpContext.Current.Request.RequestContext;
var urlHelper = new System.Web.Mvc.UrlHelper(context);
var url = string.Format("{0}{1}", context.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority), urlHelper.RouteUrl(routeName, routeValues));
///...
return new Link(linkRelation, url);
}
}
and this class is used by a MediaTypeFormatter.
This class is expected to build a link using the same host that came from the original request and leveraging any route values that were on the original request.
But... how do I get a hold of the HttpRequestMessage? This will be encapsulated by a MediaTypeFormatter - but it doesn't have one either.
There must be an easy way to get hold of the HttpRequestMessage - what am I overlooking?
thanks
Jon
I ended up creating the following base Formatter which exposes the request, now I will be able to pass it along to the LinkProvider.
public class JsonMediaTypeFormatterBase : JsonMediaTypeFormatter
{
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
Request = request;
return base.GetPerRequestFormatterInstance(type, request, mediaType);
}
protected HttpRequestMessage Request
{
get;
set;
}
}
I am setting up a simple RESTful controller for a Todo resource with an XML representation. It all works great - until I try to redirect. For example, when I POST a new Todo and attempt to redirect to its new URL (for example /todos/5, I get the following error:
Error 500 Unable to locate object to be marshalled in model: {}
I do know the POST worked because I can manually go to the new URL (/todos/5) and see the newly created resource. Its only when trying to redirect that I get the failure. I know in my example I could just return the newly created Todo object, but I have other cases where a redirect makes sense. The error looks like a marshaling problem, but like I said, it only rears itself when I add redirects to my RESTful methods, and does not occur if manually hitting the URL I am redirecting to.
A snippet of the code:
#Controller
#RequestMapping("/todos")
public class TodoController {
#RequestMapping(value="/{id}", method=GET)
public Todo getTodo(#PathVariable long id) {
return todoRepository.findById(id);
}
#RequestMapping(method=POST)
public String newTodo(#RequestBody Todo todo) {
todoRepository.save(todo); // generates and sets the ID on the todo object
return "redirect:/todos/" + todo.getId();
}
... more methods ...
public void setTodoRepository(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
private TodoRepository todoRepository;
}
Can you spot what I am missing? I am suspecting it may have something to do with returning a redirect string - perhaps instead of it triggering a redirect it is actually being passed to the XML marshaling view used by my view resolver (not shown - but typical of all the online examples), and JAXB (the configured OXM tool) doesn't know what to do with it. Just a guess...
Thanks in advance.
This happend because redirect: prefix is handled by InternalResourceViewResolver (actually, by UrlBasedViewResolver). So, if you don't have InternalResourceViewResolver or your request doesn't get into it during view resolution process, redirect is not handled.
To solve it, you can either return a RedirectView from your controller method, or add a custom view resolver for handling redirects:
public class RedirectViewResolver implements ViewResolver, Ordered {
private int order = Integer.MIN_VALUE;
public View resolveViewName(String viewName, Locale arg1) throws Exception {
if (viewName.startsWith(UrlBasedViewResolver.REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(UrlBasedViewResolver.REDIRECT_URL_PREFIX.length());
return new RedirectView(redirectUrl, true);
}
return null;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}