Irrational Exuberance!

Changing Mimetypes for Text Responses in BeepBeep

September 3, 2009. Filed under beepbeep

I'm working on a project which I am implementing in BeepBeep. Part of what I am doing is implementing an API where I return results in JSON. This is easy to accomplish if you're rendering a template,

handle_request("index",[]) ->
  {render,"home/index.html",[], [{content_type, "application/json"]}.

but there isn't a straightforward way to accomplish it when you just want to return arbitrary binary or list data instead of rendering a template. It's pretty easy to add the functionality by opening up your src/PROJECT_web.erl file and replacing loop/1 with:

loop(Req) ->
    %% Setup env...                                                                                                                                                                
    InitialEnv = mochiweb_env:setup_environment(Req),
    Env = setup_session(Req,InitialEnv),
    %% Possible return values                                                                                                                                                      
    %% {render,View,Data}                                                                                                                                                          
    %% {render,View,Data,Options}                                                                                                                                                  
    %% {text,Data}                                                                                                                                                                 
    %% {text,Data,Options}                                                                                                                                                         
    %% {json,Data}                                                                                                                                                                 
    %% {redirect,Url}                                                                                                                                                              
    %% {static,File}                                                                                                                                                               
    %% {error,_}
    case beepbeep:dispatch(Env) of
        {render,View,Data} ->
            {ok,Content} = render_template(View,Data,Env),
            Req:respond({200,
                         [{"Content-Type","text/html"}|[get_cookie(Env)]],
                         Content});
        {render,View,Data,Options} ->
            {Status,ContentType,Headers} = extract_options(Options, 200, "text/html"),
            {ok,Content} = render_template(View,Data,Env),
            Req:respond({Status,
                         [{"Content-Type",ContentType}|[get_cookie(Env)|Headers]],
                         Content});
        {text,Content} ->
            Req:respond({200,
                         [{"Content-Type","text/plain"}|[get_cookie(Env)]],
                         Content});
        {text,Content,Options} ->
            {Status,ContentType,Headers} = extract_options(Options, 200, "text/plain"),
            Req:respond({Status,
                         [{"Content-Type",ContentType}|[get_cookie(Env)|Headers]],
                         Content});
        {redirect,Url} ->
            Req:respond({302,
                         [{"Location", Url},
                          {"Content-Type", "text/html; charset=UTF-8"}],
                         ""});
        {static, File} ->
            "/" ++ StaticFile = File,
            Req:serve_file(StaticFile,skel_deps:local_path(["www"]));
        {error,_} ->
            Req:respond({500,[],"Server Error"})
    end.

And replace get_options/1 with:

extract_options(Options, DefaultStatus, DefaultMimetype) ->
    {proplists:get_value(status,Options,DefaultStatus),
     proplists:get_value(content_type,Options,DefaultMimetype),
     proplists:get_value(headers,Options,[])}.

Then you can return arbitrary data with arbitrary mimetypes.

handle_request("index",[]) ->
  Json = mochijson:encode({struct, [{a, "b"}, {b, "c"}]}),
  {text, Json, [{content_type, "application/json"]}.

You can patch your projects, or grab my patched version of BeepBeep, although hopefully we'll be able to upstream this patch without much trouble.